Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc-gpu
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-gpu
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 @@
...
@@ -4,14 +4,3 @@
* Support multiple VLC versions: define a front-end module which will
* Support multiple VLC versions: define a front-end module which will
load the appropriate versionned module from a subdirectory.
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
...
@@ -33,6 +33,7 @@ import re
import
time
import
time
import
operator
import
operator
import
itertools
import
itertools
from
optparse
import
OptionParser
# DefaultDict from ASPN python cookbook
# DefaultDict from ASPN python cookbook
import
copy
import
copy
...
@@ -91,520 +92,590 @@ special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
...
@@ -91,520 +92,590 @@ special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
parameter_passing=DefaultDict(default=1)
parameter_passing=DefaultDict(default=1)
parameter_passing['
libvlc_exception_t
*
']=3
parameter_passing['
libvlc_exception_t
*
']=3
# C-type to ctypes/python type conversion.
class Parser(object):
# Note that enum types conversions are generated (cf convert_enum_names)
def __init__(self, list_of_files):
typ2class={
self.methods=[]
'
libvlc_exception_t
*
': '
ctypes
.
POINTER
(
VLCException
)
',
self.enums=[]
'
libvlc_media_player_t
*
': '
MediaPlayer
',
for name in list_of_files:
'
libvlc_instance_t
*
': '
Instance
',
self.enums.extend(self.parse_typedef(name))
'
libvlc_media_t
*
': '
Media
',
self.methods.extend(self.parse_include(name))
'
libvlc_log_t
*
': '
Log
',
'
libvlc_log_iterator_t
*
': '
LogIterator
',
def parse_param(self, s):
'
libvlc_log_message_t
*
': '
LogMessage
',
"""Parse a C parameter expression.
'
libvlc_event_type_t
': '
ctypes
.
c_uint
',
'
libvlc_event_manager_t
*
': '
EventManager
',
It is used to parse both the type/name for methods, and type/name
'
libvlc_media_discoverer_t
*
': '
MediaDiscoverer
',
for their parameters.
'
libvlc_media_library_t
*
': '
MediaLibrary
',
'
libvlc_media_list_t
*
': '
MediaList
',
It returns a tuple (type, name).
'
libvlc_media_list_player_t
*
': '
MediaListPlayer
',
"""
'
libvlc_media_list_view_t
*
': '
MediaListView
',
s=s.strip()
'
libvlc_track_description_t
*
': '
TrackDescription
',
s=s.replace('
const
', '')
'
libvlc_audio_output_t
*
': '
AudioOutput
',
if '
VLC_FORWARD
' in s:
m=forward_re.match(s)
'
mediacontrol_Instance
*
': '
MediaControl
',
s=m.group(1)+m.group(2)
'
mediacontrol_Exception
*
': '
MediaControlException
',
m=param_re.search(s)
'
mediacontrol_RGBPicture
*
': '
ctypes
.
POINTER
(
RGBPicture
)
',
if m:
'
mediacontrol_PlaylistSeq
*
': '
MediaControlPlaylistSeq
',
const, typ, name=m.groups()
'
mediacontrol_Position
*
': '
ctypes
.
POINTER
(
MediaControlPosition
)
',
while name.startswith('
*
'):
'
mediacontrol_StreamInformation
*
': '
ctypes
.
POINTER
(
MediaControlStreamInformation
)
',
typ += '
*
'
'
WINDOWHANDLE
': '
ctypes
.
c_ulong
',
name=name[1:]
if name == '
const
*
':
'
void
': '
None
',
# K&R definition: const char * const*
'
void
*
': '
ctypes
.
c_void_p
',
name=''
'
short
': '
ctypes
.
c_short
',
typ=typ.replace('
', '')
'
char
*
': '
ctypes
.
c_char_p
',
return typ, name
'
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()
else:
else:
print l,
# K&R definition: only type
f.close()
return s.replace('
', ''), ''
def convert_enum_names(enums):
def parse_typedef(self, name):
res={}
"""Parse include file for typedef expressions.
for (typ, name, values, comment) in enums:
if typ != '
enum
':
This generates a tuple for each typedef:
raise Exception('
This
method
only
handles
enums
')
(type, name, value_list, comment)
pyname=re.findall('
(
libvlc
|
mediacontrol
)
_
(.
+
?
)(
_t
)
?$
', name)[0][1]
with type == '
enum
' (for the moment) and value_list being a list of (name, value)
if '
_
' in pyname:
Note that values are string, since this is intended for code generation.
pyname=pyname.title().replace('
_
', '')
"""
elif not pyname[0].isupper():
f=open(name, 'r')
pyname=pyname.capitalize()
accumulator=''
res[name]=pyname
for l in f:
return res
# Note: lstrip() should not be necessary, but there is 1 badly
# formatted comment in vlc1.0.0 includes
def generate_enums(enums):
if l.lstrip().startswith('
/**
'):
for (typ, name, values, comment) in enums:
comment=''
if typ != '
enum
':
continue
raise Exception('
This
method
only
handles
enums
')
elif l.startswith('
*
'):
pyname=typ2class[name]
comment = comment + l[3:]
continue
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]))
"""
def parse_typedef(name):
l=l.strip()
"""Parse include file for typedef expressions.
if l.startswith('
/*
') or l.endswith('
*/
'):
continue
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
()
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
=
[]
# Special case, used only for libvlc_events.h
for
p
in
paramlist_re
.
split
(
param
):
m=special_enum_re.match(l)
params
.
append
(
parse_param
(
p
)
)
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'
:
def
parse_include
(
self
,
name
):
# Empty parameter list
"""Parse include file.
params
=
[]
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
]):
m
=
api_re
.
match
(
l
)
# Empty parameter names. Have to poke into comment.
if
m
:
names
=
comment_re
.
findall
(
comment
)
(
ret
,
param
)
=
m
.
groups
()
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
)
]
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
:
for
typ
,
name
in
params
:
if
not
typ
in
typ2class
:
if
not
typ
in
self
.
type2class
:
raise
Exception
(
"No conversion for %s (from %s:%s)"
%
(
typ
,
method
,
name
))
raise
Exception
(
"No conversion for %s (from %s:%s)"
%
(
typ
,
met
,
name
))
# Transform Doxygen syntax into epydoc syntax
def
insert_code
(
self
,
filename
):
comment
=
comment
.
replace
(
'
\
\
param'
,
'@param'
).
replace
(
'
\
\
return'
,
'@return'
)
"""Generate header/footer code.
"""
if
debug
:
f
=
open
(
filename
,
'r'
)
print
'********************'
for
l
in
f
:
print
l
if
'build_date'
in
l
:
print
'-------->'
self
.
output
(
'build_date="%s"'
%
time
.
ctime
())
print
"%s (%s)"
%
(
method
,
rtype
)
else
:
for
typ
,
name
in
params
:
self
.
output
(
l
.
rstrip
())
print
" %s (%s)"
%
(
name
,
typ
)
f
.
close
()
print
'********************'
yield
(
rtype
,
def
convert_enum_names
(
self
,
enums
):
method
,
res
=
{}
params
,
for
(
typ
,
name
,
values
,
comment
)
in
enums
:
comment
)
if
typ
!=
'enum'
:
comment
=
''
raise
Exception
(
'This method only handles enums'
)
pyname
=
re
.
findall
(
'(libvlc|mediacontrol)_(.+?)(_t)?$'
,
name
)[
0
][
1
]
def
output_ctypes
(
rtype
,
method
,
params
,
comment
):
if
'_'
in
pyname
:
"""Output ctypes decorator for the given method.
pyname
=
pyname
.
title
().
replace
(
'_'
,
''
)
"""
elif
not
pyname
[
0
].
isupper
():
if
method
in
blacklist
:
pyname
=
pyname
.
capitalize
()
# FIXME
res
[
name
]
=
pyname
return
return
res
if
params
:
def
generate_enums
(
self
,
enums
):
print
"prototype=ctypes.CFUNCTYPE(%s, %s)"
%
(
typ2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
),
for
(
typ
,
name
,
values
,
comment
)
in
enums
:
","
.
join
(
typ2class
[
p
[
0
]]
for
p
in
params
))
if
typ
!=
'enum'
:
else
:
raise
Exception
(
'This method only handles enums'
)
print
"prototype=ctypes.CFUNCTYPE(%s)"
%
typ2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
)
pyname
=
self
.
type2class
[
name
]
self
.
output
(
"class %s(ctypes.c_uint):"
%
pyname
)
if
not
params
:
self
.
output
(
' """%s
\
n
"""'
%
comment
)
flags
=
'paramflags= tuple()'
elif
len
(
params
)
==
1
:
conv
=
{}
flags
=
"paramflags=( (%d, ), )"
%
parameter_passing
[
params
[
0
][
0
]]
# Convert symbol names
else
:
for
k
,
v
in
values
:
flags
=
"paramflags=%s"
%
","
.
join
(
'(%d,)'
%
parameter_passing
[
p
[
0
]]
for
p
in
params
)
n
=
k
.
split
(
'_'
)[
-
1
]
print
flags
if
len
(
n
)
==
1
:
print
'%s = prototype( ("%s", dll), paramflags )'
%
(
method
,
method
)
# Single character. Some symbols use 1_1, 5_1, etc.
if
'3'
in
flags
:
n
=
"_"
.
join
(
k
.
split
(
'_'
)[
-
2
:]
)
# A VLCException is present. Process it.
if
re
.
match
(
'^[0-9]'
,
n
):
print
"%s.errcheck = check_vlc_exception"
%
method
# Cannot start an identifier with a number
print
'%s.__doc__ = """%s"""'
%
(
method
,
comment
)
n
=
'_'
+
n
print
conv
[
k
]
=
n
def
parse_override
(
name
):
for
k
,
v
in
values
:
"""Parse override definitions file.
self
.
output
(
" %s=%s"
%
(
conv
[
k
],
v
))
It is possible to override methods definitions in classes.
self
.
output
(
" _names={"
)
for
k
,
v
in
values
:
It returns a tuple
self
.
output
(
" %s: '%s',"
%
(
v
,
conv
[
k
]))
(code, overriden_methods, docstring)
self
.
output
(
" }"
)
"""
code
=
{}
self
.
output
(
"""
def __repr__(self):
data
=
[]
return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
current
=
None
"""
)
f
=
open
(
name
,
'r'
)
for
l
in
f
:
def
output_ctypes
(
self
,
rtype
,
method
,
params
,
comment
):
m
=
re
.
match
(
'class (
\
S+):
'
, l)
"""Output ctypes decorator for the given method.
if m:
"""
# Dump old data
if
method
in
blacklist
:
if current is not None:
# FIXME
code[current]="".join(data)
return
current=m.group(1)
data=[]
if
params
:
continue
self
.
output
(
"prototype=ctypes.CFUNCTYPE(%s, %s)"
%
(
self
.
type2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
),
data.append(l)
","
.
join
(
self
.
type2class
[
p
[
0
]]
for
p
in
params
)))
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
else
:
else
:
o=object.__new__(cls)
self
.
output
(
"prototype=ctypes.CFUNCTYPE(%s)"
%
self
.
type2class
.
get
(
rtype
,
'FIXME_%s'
%
rtype
))
o._as_parameter_=ctypes.c_void_p(pointer)
return o
@staticmethod
def from_param(arg):
'''(INTERNAL) ctypes parameter conversion method.
'''
return arg._as_parameter_
""" % {'
name
': classname}
if classname in overrides:
if
not
params
:
print overrides[classname]
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 classname in overrides:
if method in blacklist:
self.output(overrides[classname])
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:
prefix=self.prefixes.get(classname, '')
params[0]=(params[0][0], '
self
')
if params and params[-1][0] in ('
libvlc_exception_t
*
', '
mediacontrol_Exception
*
'):
for cl, rtype, method, params, comment in el:
args=", ".join( p[1] for p in params[:-1] )
if method in blacklist:
else:
continue
args=", ".join( p[1] for p in params )
# Strip prefix
name=method.replace(prefix, '').replace('
libvlc_
', '')
print " def %s(%s):" % (name, args)
ret.add(method)
print '
"""%s
\
n
"""' % fix_python_comment(comment)
if name in overriden_methods.get(cl, []):
if params and params[-1][0] == '
libvlc_exception_t
*
':
# Method already defined in override.py
# Exception handling
continue
print " e=VLCException()"
print " return %s(%s, e)" % (method, args)
if params:
elif params and params[-1][0] == '
mediacontrol_Exception
*
':
params[0]=(params[0][0], '
self
')
# Exception handling
if params and params[-1][0] in ('
libvlc_exception_t
*
', '
mediacontrol_Exception
*
'):
print " e=MediaControlException()"
args=", ".join( p[1] for p in params[:-1] )
print " return %s(%s, e)" % (method, args)
else:
else:
args=", ".join( p[1] for p in params )
print " return %s(%s)" % (method, args)
print
self.output(" def %s(%s):" % (name, args))
self.output('
"""%s
\
n
"""' % self.fix_python_comment(comment))
# Check for standard methods
if params and params[-1][0] == '
libvlc_exception_t
*
':
if name == '
count
':
# Exception handling
# There is a count method. Generate a __len__ one.
self.output(" e=VLCException()")
print " def __len__(self):"
self.output(" return %s(%s, e)" % (method, args))
print " e=VLCException()"
elif params and params[-1][0] == '
mediacontrol_Exception
*
':
print " return %s(self, e)" % method
# Exception handling
print
self.output(" e=MediaControlException()")
elif name.endswith('
item_at_index
'):
self.output(" return %s(%s, e)" % (method, args))
# Indexable (and thus iterable)"
else:
print " def __getitem__(self, i):"
self.output(" return %s(%s)" % (method, args))
print " e=VLCException()"
self.output()
print " return %s(self, i, e)" % method
print
# Check for standard methods
print " def __iter__(self):"
if name == '
count
':
print " e=VLCException()"
# There is a count method. Generate a __len__ one.
print " for i in xrange(len(self)):"
self.output(""" def __len__(self):
print " yield self[i]"
e=VLCException()
print
return %s(self, e)
""" % method)
return ret
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__
':
if __name__ == '
__main__
':
enums=[]
opt=OptionParser(usage="""Parse VLC include files and generate bindings code.
for name in sys.argv[1:]:
%prog [options] include_file.h [...]""")
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)
insert_code('
header
.
py
')
opt.add_option("-d", "--debug", dest="debug", action="store_true",
generate_enums(enums)
default=False,
wrapped=generate_wrappers(methods)
help="Debug mode")
for l in methods:
output_ctypes(*l)
opt.add_option("-o", "--output", dest="output", action="store",
insert_code('
footer
.
py
')
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 )
g=PythonGenerator(p)
not_wrapped=all.difference(wrapped)
print "# Not wrapped methods:"
for m in not_wrapped:
print "# ", m
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