Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
109ee984
Commit
109ee984
authored
Aug 10, 2009
by
Olivier Aubert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
python-ctypes: refactor generate.py to be more modular
parent
33043ba3
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
569 additions
and
509 deletions
+569
-509
bindings/python-ctypes/TODO
bindings/python-ctypes/TODO
+0
-11
bindings/python-ctypes/generate.py
bindings/python-ctypes/generate.py
+569
-498
No files found.
bindings/python-ctypes/TODO
View file @
109ee984
...
...
@@ -4,14 +4,3 @@
* Support multiple VLC versions: define a front-end module which will
load the appropriate versionned module from a subdirectory.
* Support options: --debug/-c, --output/-o, --check-symbols/-s, ...
* Use setup.py
* Refactor code:
class Parser(list_of_includes) -> properties enums, methods...,
dump()
check()...
class PythonGenerator(parser) -> p.save( filename )
bindings/python-ctypes/generate.py
View file @
109ee984
...
...
@@ -33,6 +33,7 @@ import re
import
time
import
operator
import
itertools
from
optparse
import
OptionParser
# DefaultDict from ASPN python cookbook
import
copy
...
...
@@ -91,520 +92,590 @@ special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
parameter_passing=DefaultDict(default=1)
parameter_passing['
libvlc_exception_t
*
']=3
# C-type to ctypes/python type conversion.
# Note that enum types conversions are generated (cf convert_enum_names)
typ2class={
'
libvlc_exception_t
*
': '
ctypes
.
POINTER
(
VLCException
)
',
'
libvlc_media_player_t
*
': '
MediaPlayer
',
'
libvlc_instance_t
*
': '
Instance
',
'
libvlc_media_t
*
': '
Media
',
'
libvlc_log_t
*
': '
Log
',
'
libvlc_log_iterator_t
*
': '
LogIterator
',
'
libvlc_log_message_t
*
': '
LogMessage
',
'
libvlc_event_type_t
': '
ctypes
.
c_uint
',
'
libvlc_event_manager_t
*
': '
EventManager
',
'
libvlc_media_discoverer_t
*
': '
MediaDiscoverer
',
'
libvlc_media_library_t
*
': '
MediaLibrary
',
'
libvlc_media_list_t
*
': '
MediaList
',
'
libvlc_media_list_player_t
*
': '
MediaListPlayer
',
'
libvlc_media_list_view_t
*
': '
MediaListView
',
'
libvlc_track_description_t
*
': '
TrackDescription
',
'
libvlc_audio_output_t
*
': '
AudioOutput
',
'
mediacontrol_Instance
*
': '
MediaControl
',
'
mediacontrol_Exception
*
': '
MediaControlException
',
'
mediacontrol_RGBPicture
*
': '
ctypes
.
POINTER
(
RGBPicture
)
',
'
mediacontrol_PlaylistSeq
*
': '
MediaControlPlaylistSeq
',
'
mediacontrol_Position
*
': '
ctypes
.
POINTER
(
MediaControlPosition
)
',
'
mediacontrol_StreamInformation
*
': '
ctypes
.
POINTER
(
MediaControlStreamInformation
)
',
'
WINDOWHANDLE
': '
ctypes
.
c_ulong
',
'
void
': '
None
',
'
void
*
': '
ctypes
.
c_void_p
',
'
short
': '
ctypes
.
c_short
',
'
char
*
': '
ctypes
.
c_char_p
',
'
char
**
': '
ListPOINTER
(
ctypes
.
c_char_p
)
',
'
uint32_t
': '
ctypes
.
c_uint
',
'
float
': '
ctypes
.
c_float
',
'
unsigned
': '
ctypes
.
c_uint
',
'
int
': '
ctypes
.
c_int
',
'
...
': '
FIXMEva_list
',
'
libvlc_callback_t
': '
ctypes
.
c_void_p
',
'
libvlc_time_t
': '
ctypes
.
c_longlong
',
}
# Defined python classes, i.e. classes for which we want to generate
# class wrappers around libvlc functions
defined_classes=(
'
MediaPlayer
',
'
Instance
',
'
Media
',
'
Log
',
'
LogIterator
',
#'
LogMessage
',
'
EventManager
',
'
MediaDiscoverer
',
'
MediaLibrary
',
'
MediaList
',
'
MediaListPlayer
',
'
MediaListView
',
'
TrackDescription
',
'
AudioOutput
',
'
MediaControl
',
)
# Definition of prefixes that we can strip from method names when
# wrapping them into class methods
prefixes=dict( (v, k[:-2]) for (k, v) in typ2class.iteritems() if v in defined_classes )
prefixes['
MediaControl
']='
mediacontrol_
'
def parse_param(s):
"""Parse a C parameter expression.
It is used to parse both the type/name for methods, and type/name
for their parameters.
It returns a tuple (type, name).
"""
s=s.strip()
s=s.replace('
const
', '')
if '
VLC_FORWARD
' in s:
m=forward_re.match(s)
s=m.group(1)+m.group(2)
m=param_re.search(s)
if m:
const, typ, name=m.groups()
while name.startswith('
*
'):
typ += '
*
'
name=name[1:]
if name == '
const
*
':
# K&R definition: const char * const*
name=''
typ=typ.replace('
', '')
return typ, name
else:
# K&R definition: only type
return s.replace('
', ''), ''
def insert_code(filename):
"""Generate header/footer code.
"""
f=open(filename, 'r')
for l in f:
if '
build_date
' in l:
print '
build_date
=
"%s"' % time.ctime()
class Parser(object):
def __init__(self, list_of_files):
self.methods=[]
self.enums=[]
for name in list_of_files:
self.enums.extend(self.parse_typedef(name))
self.methods.extend(self.parse_include(name))
def parse_param(self, s):
"""Parse a C parameter expression.
It is used to parse both the type/name for methods, and type/name
for their parameters.
It returns a tuple (type, name).
"""
s=s.strip()
s=s.replace('
const
', '')
if '
VLC_FORWARD
' in s:
m=forward_re.match(s)
s=m.group(1)+m.group(2)
m=param_re.search(s)
if m:
const, typ, name=m.groups()
while name.startswith('
*
'):
typ += '
*
'
name=name[1:]
if name == '
const
*
':
# K&R definition: const char * const*
name=''
typ=typ.replace('
', '')
return typ, name
else:
print l,
f.close()
def convert_enum_names(enums):
res={}
for (typ, name, values, comment) in enums:
if typ != '
enum
':
raise Exception('
This
method
only
handles
enums
')
pyname=re.findall('
(
libvlc
|
mediacontrol
)
_
(.
+
?
)(
_t
)
?$
', name)[0][1]
if '
_
' in pyname:
pyname=pyname.title().replace('
_
', '')
elif not pyname[0].isupper():
pyname=pyname.capitalize()
res[name]=pyname
return res
def generate_enums(enums):
for (typ, name, values, comment) in enums:
if typ != '
enum
':
raise Exception('
This
method
only
handles
enums
')
pyname=typ2class[name]
print "class %s(ctypes.c_uint):" % pyname
print '
"""%s
\
n
"""' % comment
conv={}
# Convert symbol names
for k, v in values:
n=k.split('
_
')[-1]
if len(n) == 1:
# Single character. Some symbols use 1_1, 5_1, etc.
n="_".join( k.split('
_
')[-2:] )
if re.match('
^
[
0
-
9
]
', n):
# Cannot start an identifier with a number
n='
_
'+n
conv[k]=n
for k, v in values:
print " %s=%s" % (conv[k], v)
print " _names={"
for k, v in values:
print " %s: '
%
s
'," % (v, conv[k])
print " }"
print """
def __repr__(self):
return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
"""
# K&R definition: only type
return s.replace('
', ''), ''
def parse_typedef(self, name):
"""Parse include file for typedef expressions.
This generates a tuple for each typedef:
(type, name, value_list, comment)
with type == '
enum
' (for the moment) and value_list being a list of (name, value)
Note that values are string, since this is intended for code generation.
"""
f=open(name, 'r')
accumulator=''
for l in f:
# Note: lstrip() should not be necessary, but there is 1 badly
# formatted comment in vlc1.0.0 includes
if l.lstrip().startswith('
/**
'):
comment=''
continue
elif l.startswith('
*
'):
comment = comment + l[3:]
continue
def parse_typedef(name):
"""Parse include file for typedef expressions.
This generates a tuple for each typedef:
(type, name, value_list, comment)
with type == '
enum
' (for the moment) and value_list being a list of (name, value)
Note that values are string, since this is intended for code generation.
"""
f=open(name, 'r')
accumulator=''
for l in f:
# Note: lstrip() should not be necessary, but there is 1 badly
# formatted comment in vlc1.0.0 includes
if l.lstrip().startswith('
/**
'):
comment=''
continue
elif l.startswith('
*
'):
comment = comment + l[3:]
continue
l=l.strip()
if l.startswith('
/*
') or l.endswith('
*/
'):
continue
if (l.startswith('
typedef
enum
') or l.startswith('
enum
')) and not l.endswith('
;
'):
# Multiline definition. Accumulate until end of definition
accumulator=l
continue
elif accumulator:
accumulator=" ".join( (accumulator, l) )
if l.endswith('
;
'):
# End of definition
l=accumulator
accumulator=''
m=enum_re.match(l)
if m:
values=[]
(typ, dummy, data, name)=m.groups()
for i, l in enumerate(paramlist_re.split(data)):
l=l.strip()
if l.startswith('
/*
'):
continue
if '
=
' in l:
# A value was specified. Use it.
values.append(re.split('
\
s
*=
\
s
*
', l))
else:
if l:
values.append( (l, str(i)) )
comment=comment.replace('
@
{
', '').replace('
@
see
', '
See
').replace('
\
ingroup
', '')
yield (typ, name.strip(), values, comment)
comment=''
continue
# Special case, used only for libvlc_events.h
m=special_enum_re.match(l)
if m:
values=[]
(typ, name, data)=m.groups()
for i, l in enumerate(paramlist_re.split(data)):
l=l.strip()
if l.startswith('
/*
') or l.startswith('
#'):
continue
if
'='
in
l
:
# A value was specified. Use it.
values
.
append
(
re
.
split
(
'
\
s*=
\
s*'
,
l
))
else
:
if
l
:
values
.
append
(
(
l
,
str
(
i
))
)
comment
=
comment
.
replace
(
'@{'
,
''
).
replace
(
'@see'
,
'See'
).
replace
(
'
\
i
n
group'
,
''
)
yield
(
typ
,
name
.
strip
(),
values
,
comment
)
comment
=
''
continue
def
parse_include
(
name
):
"""Parse include file.
This generates a tuple for each function:
(return_type, method_name, parameter_list, comment)
with parameter_list being a list of tuples (parameter_type, parameter_name).
"""
f
=
open
(
name
,
'r'
)
accumulator
=
''
comment
=
''
for
l
in
f
:
# Note: lstrip() should not be necessary, but there is 1 badly
# formatted comment in vlc1.0.0 includes
if
l
.
lstrip
().
startswith
(
'/**'
):
comment
=
''
continue
elif
l
.
startswith
(
' * '
):
comment
=
comment
+
l
[
3
:]
continue
l
=
l
.
strip
()
if
accumulator
:
accumulator
=
" "
.
join
(
(
accumulator
,
l
)
)
if
l
.
endswith
(
');'
):
# End of definition
l
=
accumulator
accumulator
=
''
elif
l
.
startswith
(
'VLC_PUBLIC_API'
)
and
not
l
.
endswith
(
');'
):
# Multiline definition. Accumulate until end of definition
accumulator
=
l
continue
m
=
api_re
.
match
(
l
)
if
m
:
(
ret
,
param
)
=
m
.
groups
()
l=l.strip()
if l.startswith('
/*
') or l.endswith('
*/
'):
continue
rtype
,
method
=
parse_param
(
ret
)
if (l.startswith('
typedef
enum
') or l.startswith('
enum
')) and not l.endswith('
;
'):
# Multiline definition. Accumulate until end of definition
accumulator=l
continue
elif accumulator:
accumulator=" ".join( (accumulator, l) )
if l.endswith('
;
'):
# End of definition
l=accumulator
accumulator=''
m=enum_re.match(l)
if m:
values=[]
(typ, dummy, data, name)=m.groups()
for i, l in enumerate(paramlist_re.split(data)):
l=l.strip()
if l.startswith('
/*
'):
continue
if '
=
' in l:
# A value was specified. Use it.
values.append(re.split('
\
s
*=
\
s
*
', l))
else:
if l:
values.append( (l, str(i)) )
comment=comment.replace('
@
{
', '').replace('
@
see
', '
See
').replace('
\
ingroup
', '')
yield (typ, name.strip(), values, comment)
comment=''
continue
params
=
[]
for
p
in
paramlist_re
.
split
(
param
):
params
.
append
(
parse_param
(
p
)
)
# Special case, used only for libvlc_events.h
m=special_enum_re.match(l)
if m:
values=[]
(typ, name, data)=m.groups()
for i, l in enumerate(paramlist_re.split(data)):
l=l.strip()
if l.startswith('
/*
') or l.startswith('
#'):
continue
if
'='
in
l
:
# A value was specified. Use it.
values
.
append
(
re
.
split
(
'
\
s*=
\
s*'
,
l
))
else
:
if
l
:
values
.
append
(
(
l
,
str
(
i
))
)
comment
=
comment
.
replace
(
'@{'
,
''
).
replace
(
'@see'
,
'See'
).
replace
(
'
\
i
n
group'
,
''
)
yield
(
typ
,
name
.
strip
(),
values
,
comment
)
comment
=
''
continue
if
len
(
params
)
==
1
and
params
[
0
][
0
]
==
'void'
:
# Empty parameter list
params
=
[]
def
parse_include
(
self
,
name
):
"""Parse include file.
This generates a tuple for each function:
(return_type, method_name, parameter_list, comment)
with parameter_list being a list of tuples (parameter_type, parameter_name).
"""
f
=
open
(
name
,
'r'
)
accumulator
=
''
comment
=
''
for
l
in
f
:
# Note: lstrip() should not be necessary, but there is 1 badly
# formatted comment in vlc1.0.0 includes
if
l
.
lstrip
().
startswith
(
'/**'
):
comment
=
''
continue
elif
l
.
startswith
(
' * '
):
comment
=
comment
+
l
[
3
:]
continue
l
=
l
.
strip
()
if
accumulator
:
accumulator
=
" "
.
join
(
(
accumulator
,
l
)
)
if
l
.
endswith
(
');'
):
# End of definition
l
=
accumulator
accumulator
=
''
elif
l
.
startswith
(
'VLC_PUBLIC_API'
)
and
not
l
.
endswith
(
');'
):
# Multiline definition. Accumulate until end of definition
accumulator
=
l
continue
if
list
(
p
for
p
in
params
if
not
p
[
1
]):
# Empty parameter names. Have to poke into comment.
names
=
comment_re
.
findall
(
comment
)
if
len
(
names
)
<
len
(
params
):
# Bad description: all parameters are not specified.
# Generate default parameter names
badnames
=
[
"param%d"
%
i
for
i
in
xrange
(
len
(
params
))
]
# Put in the existing ones
for
(
i
,
p
)
in
enumerate
(
names
):
badnames
[
i
]
=
names
[
i
]
names
=
badnames
print
"### Error ###"
print
"### Cannot get parameter names from comment for %s: %s"
%
(
method
,
comment
.
replace
(
"
\
n
"
,
' '
))
# Note: this was previously
# raise Exception("Cannot get parameter names from comment for %s: %s" % (method, comment))
# but it prevented code generation for a minor detail (some bad descriptions).
params
=
[
(
p
[
0
],
names
[
i
])
for
(
i
,
p
)
in
enumerate
(
params
)
]
m
=
api_re
.
match
(
l
)
if
m
:
(
ret
,
param
)
=
m
.
groups
()
rtype
,
method
=
self
.
parse_param
(
ret
)
params
=
[]
for
p
in
paramlist_re
.
split
(
param
):
params
.
append
(
self
.
parse_param
(
p
)
)
if
len
(
params
)
==
1
and
params
[
0
][
0
]
==
'void'
:
# Empty parameter list
params
=
[]
if
list
(
p
for
p
in
params
if
not
p
[
1
]):
# Empty parameter names. Have to poke into comment.
names
=
comment_re
.
findall
(
comment
)
if
len
(
names
)
<
len
(
params
):
# Bad description: all parameters are not specified.
# Generate default parameter names
badnames
=
[
"param%d"
%
i
for
i
in
xrange
(
len
(
params
))
]
# Put in the existing ones
for
(
i
,
p
)
in
enumerate
(
names
):
badnames
[
i
]
=
names
[
i
]
names
=
badnames
print
"### Error ###"
print
"### Cannot get parameter names from comment for %s: %s"
%
(
method
,
comment
.
replace
(
"
\
n
"
,
' '
))
# Note: this was previously
# raise Exception("Cannot get parameter names from comment for %s: %s" % (method, comment))
# but it prevented code generation for a minor detail (some bad descriptions).
params
=
[
(
p
[
0
],
names
[
i
])
for
(
i
,
p
)
in
enumerate
(
params
)
]
# Transform Doxygen syntax into epydoc syntax
comment
=
comment
.
replace
(
'
\
\
param'
,
'@param'
).
replace
(
'
\
\
return'
,
'@return'
)
if
debug
:
print
'********************'
print
l
print
'-------->'
print
"%s (%s)"
%
(
method
,
rtype
)
for
typ
,
name
in
params
:
print
" %s (%s)"
%
(
name
,
typ
)
print
'********************'
yield
(
rtype
,
method
,
params
,
comment
)
comment
=
''
def
dump_methods
(
self
):
print
"** Defined functions **"
for
(
rtype
,
name
,
params
,
comment
)
in
self
.
methods
:
print
"%(name)s (%(rtype)s):"
%
locals
()
for
t
,
n
in
params
:
print
" %(n)s (%(t)s)"
%
locals
()
def
dump_enums
(
self
):
print
"** Defined enums **"
for
(
typ
,
name
,
values
,
comment
)
in
self
.
enums
:
print
"%(name)s (%(typ)s):"
%
locals
()
for
k
,
v
in
values
:
print
" %(k)s=%(v)s"
%
locals
()
class
PythonGenerator
(
object
):
# C-type to ctypes/python type conversion.
# Note that enum types conversions are generated (cf convert_enum_names)
type2class
=
{
'libvlc_exception_t*'
:
'ctypes.POINTER(VLCException)'
,
'libvlc_media_player_t*'
:
'MediaPlayer'
,
'libvlc_instance_t*'
:
'Instance'
,
'libvlc_media_t*'
:
'Media'
,
'libvlc_log_t*'
:
'Log'
,
'libvlc_log_iterator_t*'
:
'LogIterator'
,
'libvlc_log_message_t*'
:
'LogMessage'
,
'libvlc_event_type_t'
:
'ctypes.c_uint'
,
'libvlc_event_manager_t*'
:
'EventManager'
,
'libvlc_media_discoverer_t*'
:
'MediaDiscoverer'
,
'libvlc_media_library_t*'
:
'MediaLibrary'
,
'libvlc_media_list_t*'
:
'MediaList'
,
'libvlc_media_list_player_t*'
:
'MediaListPlayer'
,
'libvlc_media_list_view_t*'
:
'MediaListView'
,
'libvlc_track_description_t*'
:
'TrackDescription'
,
'libvlc_audio_output_t*'
:
'AudioOutput'
,
'mediacontrol_Instance*'
:
'MediaControl'
,
'mediacontrol_Exception*'
:
'MediaControlException'
,
'mediacontrol_RGBPicture*'
:
'ctypes.POINTER(RGBPicture)'
,
'mediacontrol_PlaylistSeq*'
:
'MediaControlPlaylistSeq'
,
'mediacontrol_Position*'
:
'ctypes.POINTER(MediaControlPosition)'
,
'mediacontrol_StreamInformation*'
:
'ctypes.POINTER(MediaControlStreamInformation)'
,
'WINDOWHANDLE'
:
'ctypes.c_ulong'
,
'void'
:
'None'
,
'void*'
:
'ctypes.c_void_p'
,
'short'
:
'ctypes.c_short'
,
'char*'
:
'ctypes.c_char_p'
,
'char**'
:
'ListPOINTER(ctypes.c_char_p)'
,
'uint32_t'
:
'ctypes.c_uint'
,
'float'
:
'ctypes.c_float'
,
'unsigned'
:
'ctypes.c_uint'
,
'int'
:
'ctypes.c_int'
,
'...'
:
'FIXMEva_list'
,
'libvlc_callback_t'
:
'ctypes.c_void_p'
,
'libvlc_time_t'
:
'ctypes.c_longlong'
,
}
# Defined python classes, i.e. classes for which we want to generate
# class wrappers around libvlc functions
defined_classes
=
(
'MediaPlayer'
,
'Instance'
,
'Media'
,
'Log'
,
'LogIterator'
,
'EventManager'
,
'MediaDiscoverer'
,
'MediaLibrary'
,
'MediaList'
,
'MediaListPlayer'
,
'MediaListView'
,
'TrackDescription'
,
'AudioOutput'
,
'MediaControl'
,
)
def
__init__
(
self
,
parser
=
None
):
self
.
parser
=
parser
# Generate python names for enums
self
.
type2class
.
update
(
self
.
convert_enum_names
(
parser
.
enums
))
self
.
check_types
()
# Definition of prefixes that we can strip from method names when
# wrapping them into class methods
self
.
prefixes
=
dict
(
(
v
,
k
[:
-
2
])
for
(
k
,
v
)
in
self
.
type2class
.
iteritems
()
if
v
in
self
.
defined_classes
)
self
.
prefixes
[
'MediaControl'
]
=
'mediacontrol_'
def
save
(
self
,
filename
=
None
):
if
filename
is
None
or
filename
==
'-'
:
self
.
fd
=
sys
.
stdout
else
:
self
.
fd
=
open
(
filename
,
'w'
)
self
.
insert_code
(
'header.py'
)
self
.
generate_enums
(
self
.
parser
.
enums
)
wrapped_methods
=
self
.
generate_wrappers
(
self
.
parser
.
methods
)
for
l
in
self
.
parser
.
methods
:
self
.
output_ctypes
(
*
l
)
self
.
insert_code
(
'footer.py'
)
all_methods
=
set
(
t
[
1
]
for
t
in
self
.
parser
.
methods
)
not_wrapped
=
all_methods
.
difference
(
wrapped_methods
)
self
.
output
(
"# Not wrapped methods:"
)
for
m
in
not_wrapped
:
self
.
output
(
"# "
,
m
)
if
self
.
fd
!=
sys
.
stdout
:
self
.
fd
.
close
()
def
output
(
self
,
*
p
):
self
.
fd
.
write
(
" "
.
join
(
p
))
self
.
fd
.
write
(
"
\
n
"
)
def
check_types
(
self
):
"""Make sure that all types are properly translated.
This method must be called *after* convert_enum_names, since
the latter populates type2class with converted enum names.
"""
for
(
rt
,
met
,
params
,
c
)
in
self
.
parser
.
methods
:
for
typ
,
name
in
params
:
if
not
typ
in
typ2class
:
raise
Exception
(
"No conversion for %s (from %s:%s)"
%
(
typ
,
method
,
name
))
# Transform Doxygen syntax into epydoc syntax
comment
=
comment
.
replace
(
'
\
\
param'
,
'@param'
).
replace
(
'
\
\
return'
,
'@return'
)
if
debug
:
print
'********************'
print
l
print
'-------->'
print
"%s (%s)"
%
(
method
,
rtype
)
for
typ
,
name
in
params
:
print
" %s (%s)"
%
(
name
,
typ
)
print
'********************'
yield
(
rtype
,
method
,
params
,
comment
)
comment
=
''
def
output_ctypes
(
rtype
,
method
,
params
,
comment
):
"""Output ctypes decorator for the given method.
"""
if
method
in
blacklist
:
# FIXME
return
if
params
:
print
"prototype=ctypes.CFUNCTYPE(%s, %s)"
%
(
typ2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
),
","
.
join
(
typ2class
[
p
[
0
]]
for
p
in
params
))
else
:
print
"prototype=ctypes.CFUNCTYPE(%s)"
%
typ2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
)
if
not
params
:
flags
=
'paramflags= tuple()'
elif
len
(
params
)
==
1
:
flags
=
"paramflags=( (%d, ), )"
%
parameter_passing
[
params
[
0
][
0
]]
else
:
flags
=
"paramflags=%s"
%
","
.
join
(
'(%d,)'
%
parameter_passing
[
p
[
0
]]
for
p
in
params
)
print
flags
print
'%s = prototype( ("%s", dll), paramflags )'
%
(
method
,
method
)
if
'3'
in
flags
:
# A VLCException is present. Process it.
print
"%s.errcheck = check_vlc_exception"
%
method
print
'%s.__doc__ = """%s"""'
%
(
method
,
comment
)
print
def
parse_override
(
name
):
"""Parse override definitions file.
It is possible to override methods definitions in classes.
It returns a tuple
(code, overriden_methods, docstring)
"""
code
=
{}
data
=
[]
current
=
None
f
=
open
(
name
,
'r'
)
for
l
in
f
:
m
=
re
.
match
(
'class (
\
S+):
'
, l)
if m:
# Dump old data
if current is not None:
code[current]="".join(data)
current=m.group(1)
data=[]
continue
data.append(l)
code[current]="".join(data)
f.close()
docstring={}
for k, v in code.iteritems():
if v.lstrip().startswith('"""'):
# Starting comment. Use it as docstring.
dummy, docstring[k], code[k]=v.split('"""', 2)
# Not robust wrt. internal methods, but this works for the moment.
overridden_methods=dict( (k, re.findall('
^
\
s
+
def
\
s
+
(
\
w
+
)
', v, re.MULTILINE)) for (k, v) in code.iteritems() )
return code, overridden_methods, docstring
def fix_python_comment(c):
"""Fix comment by removing first and last parameters (self and exception)
"""
data=c.replace('
@
{
', '').replace('
@
see
', '
See
').splitlines()
body=itertools.takewhile(lambda l: not '
@
param
' in l and not '
@
return
' in l, data)
param=[ python_param_re.sub('
\\
1
:
\\
2
', l) for l in itertools.ifilter(lambda l: '
@
param
' in l, data) ]
ret=[ l.replace('
@
return
', '
@
return
:
') for l in itertools.ifilter(lambda l: '
@
return
' in l, data) ]
if len(param) >= 2:
param=param[1:-1]
elif len(param) == 1:
param=[]
return "
\
n
".join(itertools.chain(body, param, ret))
def generate_wrappers(methods):
"""Generate class wrappers for all appropriate methods.
@return: the set of wrapped method names
"""
ret=set()
# Sort methods against the element they apply to.
elements=sorted( ( (typ2class.get(params[0][0]), rt, met, params, c)
for (rt, met, params, c) in methods
if params and typ2class.get(params[0][0], '
_
') in defined_classes
),
key=operator.itemgetter(0))
overrides, overriden_methods, docstring=parse_override('
override
.
py
')
for classname, el in itertools.groupby(elements, key=operator.itemgetter(0)):
print """class %(name)s(object):""" % {'
name
': classname}
if classname in docstring:
print '
"""%s
\
n
"""' % docstring[classname]
print """
def __new__(cls, pointer=None):
'''Internal method used for instanciating wrappers from ctypes.
'''
if pointer is None:
raise Exception("Internal method. You should instanciate objects through other class methods (probably named '
new
' or ending with '
new
')")
if pointer == 0:
return None
if
not
typ
in
self
.
type2class
:
raise
Exception
(
"No conversion for %s (from %s:%s)"
%
(
typ
,
met
,
name
))
def
insert_code
(
self
,
filename
):
"""Generate header/footer code.
"""
f
=
open
(
filename
,
'r'
)
for
l
in
f
:
if
'build_date'
in
l
:
self
.
output
(
'build_date="%s"'
%
time
.
ctime
())
else
:
self
.
output
(
l
.
rstrip
())
f
.
close
()
def
convert_enum_names
(
self
,
enums
):
res
=
{}
for
(
typ
,
name
,
values
,
comment
)
in
enums
:
if
typ
!=
'enum'
:
raise
Exception
(
'This method only handles enums'
)
pyname
=
re
.
findall
(
'(libvlc|mediacontrol)_(.+?)(_t)?$'
,
name
)[
0
][
1
]
if
'_'
in
pyname
:
pyname
=
pyname
.
title
().
replace
(
'_'
,
''
)
elif
not
pyname
[
0
].
isupper
():
pyname
=
pyname
.
capitalize
()
res
[
name
]
=
pyname
return
res
def
generate_enums
(
self
,
enums
):
for
(
typ
,
name
,
values
,
comment
)
in
enums
:
if
typ
!=
'enum'
:
raise
Exception
(
'This method only handles enums'
)
pyname
=
self
.
type2class
[
name
]
self
.
output
(
"class %s(ctypes.c_uint):"
%
pyname
)
self
.
output
(
' """%s
\
n
"""'
%
comment
)
conv
=
{}
# Convert symbol names
for
k
,
v
in
values
:
n
=
k
.
split
(
'_'
)[
-
1
]
if
len
(
n
)
==
1
:
# Single character. Some symbols use 1_1, 5_1, etc.
n
=
"_"
.
join
(
k
.
split
(
'_'
)[
-
2
:]
)
if
re
.
match
(
'^[0-9]'
,
n
):
# Cannot start an identifier with a number
n
=
'_'
+
n
conv
[
k
]
=
n
for
k
,
v
in
values
:
self
.
output
(
" %s=%s"
%
(
conv
[
k
],
v
))
self
.
output
(
" _names={"
)
for
k
,
v
in
values
:
self
.
output
(
" %s: '%s',"
%
(
v
,
conv
[
k
]))
self
.
output
(
" }"
)
self
.
output
(
"""
def __repr__(self):
return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
"""
)
def
output_ctypes
(
self
,
rtype
,
method
,
params
,
comment
):
"""Output ctypes decorator for the given method.
"""
if
method
in
blacklist
:
# FIXME
return
if
params
:
self
.
output
(
"prototype=ctypes.CFUNCTYPE(%s, %s)"
%
(
self
.
type2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
),
","
.
join
(
self
.
type2class
[
p
[
0
]]
for
p
in
params
)))
else
:
o=object.__new__(cls)
o._as_parameter_=ctypes.c_void_p(pointer)
return o
self
.
output
(
"prototype=ctypes.CFUNCTYPE(%s)"
%
self
.
type2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
))
@staticmethod
def from_param(arg):
'''(INTERNAL) ctypes parameter conversion method.
'''
return arg._as_parameter_
""" % {'
name
': classname}
if classname in overrides:
print overrides[classname]
if
not
params
:
flags
=
'paramflags= tuple()'
elif
len
(
params
)
==
1
:
flags
=
"paramflags=( (%d, ), )"
%
parameter_passing
[
params
[
0
][
0
]]
else
:
flags
=
"paramflags=%s"
%
","
.
join
(
'(%d,)'
%
parameter_passing
[
p
[
0
]]
for
p
in
params
)
self
.
output
(
flags
)
self
.
output
(
'%s = prototype( ("%s", dll), paramflags )'
%
(
method
,
method
))
if
'3'
in
flags
:
# A VLCException is present. Process it.
self
.
output
(
"%s.errcheck = check_vlc_exception"
%
method
)
self
.
output
(
'%s.__doc__ = """%s"""'
%
(
method
,
comment
))
self
.
output
()
def
parse_override
(
self
,
name
):
"""Parse override definitions file.
It is possible to override methods definitions in classes.
It returns a tuple
(code, overriden_methods, docstring)
"""
code
=
{}
data
=
[]
current
=
None
f
=
open
(
name
,
'r'
)
for
l
in
f
:
m
=
re
.
match
(
'class (
\
S+):
'
, l)
if m:
# Dump old data
if current is not None:
code[current]="".join(data)
current=m.group(1)
data=[]
continue
data.append(l)
code[current]="".join(data)
f.close()
docstring={}
for k, v in code.iteritems():
if v.lstrip().startswith('"""'):
# Starting comment. Use it as docstring.
dummy, docstring[k], code[k]=v.split('"""', 2)
# Not robust wrt. internal methods, but this works for the moment.
overridden_methods=dict( (k, re.findall('
^
\
s
+
def
\
s
+
(
\
w
+
)
', v, re.MULTILINE)) for (k, v) in code.iteritems() )
return code, overridden_methods, docstring
def fix_python_comment(self, c):
"""Fix comment by removing first and last parameters (self and exception)
"""
data=c.replace('
@
{
', '').replace('
@
see
', '
See
').splitlines()
body=itertools.takewhile(lambda l: not '
@
param
' in l and not '
@
return
' in l, data)
param=[ python_param_re.sub('
\\
1
:
\\
2
', l) for l in itertools.ifilter(lambda l: '
@
param
' in l, data) ]
ret=[ l.replace('
@
return
', '
@
return
:
') for l in itertools.ifilter(lambda l: '
@
return
' in l, data) ]
if len(param) >= 2:
param=param[1:-1]
elif len(param) == 1:
param=[]
return "
\
n
".join(itertools.chain(body, param, ret))
def generate_wrappers(self, methods):
"""Generate class wrappers for all appropriate methods.
@return: the set of wrapped method names
"""
ret=set()
# Sort methods against the element they apply to.
elements=sorted( ( (self.type2class.get(params[0][0]), rt, met, params, c)
for (rt, met, params, c) in methods
if params and self.type2class.get(params[0][0], '
_
') in self.defined_classes
),
key=operator.itemgetter(0))
overrides, overriden_methods, docstring=self.parse_override('
override
.
py
')
for classname, el in itertools.groupby(elements, key=operator.itemgetter(0)):
self.output("""class %(name)s(object):""" % {'
name
': classname})
if classname in docstring:
self.output('
"""%s
\
n
"""' % docstring[classname])
self.output("""
def __new__(cls, pointer=None):
'''Internal method used for instanciating wrappers from ctypes.
'''
if pointer is None:
raise Exception("Internal method. Surely this class cannot be instanciated by itself.")
if pointer == 0:
return None
else:
o=object.__new__(cls)
o._as_parameter_=ctypes.c_void_p(pointer)
return o
prefix=prefixes.get(classname, '')
@staticmethod
def from_param(arg):
'''(INTERNAL) ctypes parameter conversion method.
'''
return arg._as_parameter_
""" % {'
name
': classname})
for cl, rtype, method, params, comment in el:
if method in blacklist:
continue
# Strip prefix
name=method.replace(prefix, '').replace('
libvlc_
', '')
ret.add(method)
if name in overriden_methods.get(cl, []):
# Method already defined in override.py
continue
if classname in overrides:
self.output(overrides[classname])
if params:
params[0]=(params[0][0], '
self
')
if params and params[-1][0] in ('
libvlc_exception_t
*
', '
mediacontrol_Exception
*
'):
args=", ".join( p[1] for p in params[:-1] )
else:
args=", ".join( p[1] for p in params )
print " def %s(%s):" % (name, args)
print '
"""%s
\
n
"""' % fix_python_comment(comment)
if params and params[-1][0] == '
libvlc_exception_t
*
':
# Exception handling
print " e=VLCException()"
print " return %s(%s, e)" % (method, args)
elif params and params[-1][0] == '
mediacontrol_Exception
*
':
# Exception handling
print " e=MediaControlException()"
print " return %s(%s, e)" % (method, args)
else:
print " return %s(%s)" % (method, args)
print
# Check for standard methods
if name == '
count
':
# There is a count method. Generate a __len__ one.
print " def __len__(self):"
print " e=VLCException()"
print " return %s(self, e)" % method
print
elif name.endswith('
item_at_index
'):
# Indexable (and thus iterable)"
print " def __getitem__(self, i):"
print " e=VLCException()"
print " return %s(self, i, e)" % method
print
print " def __iter__(self):"
print " e=VLCException()"
print " for i in xrange(len(self)):"
print " yield self[i]"
print
return ret
prefix=self.prefixes.get(classname, '')
for cl, rtype, method, params, comment in el:
if method in blacklist:
continue
# Strip prefix
name=method.replace(prefix, '').replace('
libvlc_
', '')
ret.add(method)
if name in overriden_methods.get(cl, []):
# Method already defined in override.py
continue
if params:
params[0]=(params[0][0], '
self
')
if params and params[-1][0] in ('
libvlc_exception_t
*
', '
mediacontrol_Exception
*
'):
args=", ".join( p[1] for p in params[:-1] )
else:
args=", ".join( p[1] for p in params )
self.output(" def %s(%s):" % (name, args))
self.output('
"""%s
\
n
"""' % self.fix_python_comment(comment))
if params and params[-1][0] == '
libvlc_exception_t
*
':
# Exception handling
self.output(" e=VLCException()")
self.output(" return %s(%s, e)" % (method, args))
elif params and params[-1][0] == '
mediacontrol_Exception
*
':
# Exception handling
self.output(" e=MediaControlException()")
self.output(" return %s(%s, e)" % (method, args))
else:
self.output(" return %s(%s)" % (method, args))
self.output()
# Check for standard methods
if name == '
count
':
# There is a count method. Generate a __len__ one.
self.output(""" def __len__(self):
e=VLCException()
return %s(self, e)
""" % method)
elif name.endswith('
item_at_index
'):
# Indexable (and thus iterable)"
self.output(""" def __getitem__(self, i):
e=VLCException()
return %s(self, i, e)
def __iter__(self):
e=VLCException()
for i in xrange(len(self)):
yield self[i]
""" % method)
return ret
def process(output, list_of_includes):
p=Parser(list_of_includes)
g=PythonGenerator(p)
g.save(output)
if __name__ == '
__main__
':
enums=[]
for name in sys.argv[1:]:
enums.extend(list(parse_typedef(name)))
# Generate python names for enums
typ2class.update(convert_enum_names(enums))
methods=[]
for name in sys.argv[1:]:
methods.extend(list(parse_include(name)))
if debug:
sys.exit(0)
opt=OptionParser(usage="""Parse VLC include files and generate bindings code.
%prog [options] include_file.h [...]""")
insert_code('
header
.
py
')
generate_enums(enums)
wrapped=generate_wrappers(methods)
for l in methods:
output_ctypes(*l)
insert_code('
footer
.
py
')
opt.add_option("-d", "--debug", dest="debug", action="store_true",
default=False,
help="Debug mode")
opt.add_option("-o", "--output", dest="output", action="store",
type="str", default="-",
help="Output filename")
(options, args) = opt.parse_args()
if not args:
opt.print_help()
sys.exit(1)
p=Parser(args)
if options.debug:
p.dump_methods()
p.dump_enums()
sys.exit(0)
all=set( t[1] for t in methods )
not_wrapped=all.difference(wrapped)
print "# Not wrapped methods:"
for m in not_wrapped:
print "# ", m
g=PythonGenerator(p)
g.save(options.output)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment