5 # Code generator for python ctypes bindings for VLC
6 # Copyright (C) 2009 the VideoLAN team
9 # Authors: Olivier Aubert <olivier.aubert at liris.cnrs.fr>
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 """This module parses VLC public API include files and generates
27 corresponding python/ctypes code. Moreover, it generates class
28 wrappers for most methods.
37 from optparse import OptionParser
39 # DefaultDict from ASPN python cookbook
41 class DefaultDict(dict):
42 """Dictionary with a default value for unknown keys."""
43 def __init__(self, default=None, **items):
44 dict.__init__(self, **items)
45 self.default = default
47 def __getitem__(self, key):
51 ## Need copy in case self.default is something like []
52 return self.setdefault(key, copy.deepcopy(self.default))
55 return DefaultDict(self.default, **self)
57 # Methods not decorated/not referenced
59 "libvlc_exception_raise",
60 "libvlc_exception_raised",
61 "libvlc_exception_get_message",
62 "libvlc_get_vlc_instance",
64 "libvlc_media_list_view_index_of_item",
65 "libvlc_media_list_view_insert_at_index",
66 "libvlc_media_list_view_remove_at_index",
67 "libvlc_media_list_view_add_item",
69 # In svn but not in current 1.0.0.
70 #"libvlc_media_add_option_flag",
71 #'libvlc_video_set_deinterlace',
72 #'libvlc_video_get_marquee_option_as_int',
73 #'libvlc_video_get_marquee_option_as_string',
74 #'libvlc_video_set_marquee_option_as_int',
75 #'libvlc_video_set_marquee_option_as_string',
76 #'libvlc_vlm_get_event_manager',
77 #"libvlc_media_list_player_event_manager",
78 #'libvlc_media_player_next_frame',
80 'mediacontrol_PlaylistSeq__free',
84 api_re=re.compile('VLC_PUBLIC_API\s+(\S+\s+.+?)\s*\(\s*(.+?)\s*\)')
85 param_re=re.compile('\s*(const\s*|unsigned\s*|struct\s*)?(\S+\s*\**)\s+(.+)')
86 paramlist_re=re.compile('\s*,\s*')
87 comment_re=re.compile('\\param\s+(\S+)')
88 python_param_re=re.compile('(@param\s+\S+)(.+)')
89 forward_re=re.compile('.+\(\s*(.+?)\s*\)(\s*\S+)')
90 enum_re=re.compile('typedef\s+(enum)\s*(\S+\s*)?\{\s*(.+)\s*\}\s*(\S+);')
91 special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
92 event_def_re=re.compile('^DEF\(\s*(\w+)\s*\)')
94 # Definition of parameter passing mode for types. This should not be
95 # hardcoded this way, but works alright ATM.
96 parameter_passing=DefaultDict(default=1)
97 parameter_passing['libvlc_exception_t*']=3
100 def __init__(self, list_of_files):
104 for name in list_of_files:
105 self.enums.extend(self.parse_typedef(name))
106 self.methods.extend(self.parse_include(name))
108 def parse_param(self, s):
109 """Parse a C parameter expression.
111 It is used to parse both the type/name for methods, and type/name
112 for their parameters.
114 It returns a tuple (type, name).
117 s=s.replace('const', '')
118 if 'VLC_FORWARD' in s:
119 m=forward_re.match(s)
120 s=m.group(1)+m.group(2)
123 const, typ, name=m.groups()
124 while name.startswith('*'):
128 # K&R definition: const char * const*
130 typ=typ.replace(' ', '')
133 # K&R definition: only type
134 return s.replace(' ', ''), ''
136 def parse_typedef(self, name):
137 """Parse include file for typedef expressions.
139 This generates a tuple for each typedef:
140 (type, name, value_list, comment)
141 with type == 'enum' (for the moment) and value_list being a list of (name, value)
142 Note that values are string, since this is intended for code generation.
149 # Note: lstrip() should not be necessary, but there is 1 badly
150 # formatted comment in vlc1.0.0 includes
151 if l.lstrip().startswith('/**'):
154 elif l.startswith(' * '):
155 comment = comment + l[3:]
159 if l.startswith('/*') or l.endswith('*/'):
162 if (l.startswith('typedef enum') or l.startswith('enum')) and not l.endswith(';'):
163 # Multiline definition. Accumulate until end of definition
167 accumulator=" ".join( (accumulator, l) )
176 (typ, dummy, data, name)=m.groups()
177 for i, l in enumerate(paramlist_re.split(data)):
179 if l.startswith('/*'):
182 # A value was specified. Use it.
183 values.append(re.split('\s*=\s*', l))
186 values.append( (l, str(i)) )
187 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
188 yield (typ, name.strip(), values, comment)
192 # Special case, used only for libvlc_events.h
193 # (version after 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
194 m=event_def_re.match(l)
197 event_names.append('libvlc_'+m.group(1))
200 # Special case, used only for libvlc_events.h
201 m=special_enum_re.match(l)
203 (typ, name, data)=m.groups()
205 # event_names were defined through DEF macro
206 # (see 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
207 values=list( (n, str(i)) for i, n in enumerate(event_names))
209 # Before 96a96f60bb0d1f2506e68b356897ceca6f6b586d
211 for i, l in enumerate(paramlist_re.split(data)):
213 if l.startswith('/*') or l.startswith('#'):
216 # A value was specified. Use it.
217 values.append(re.split('\s*=\s*', l))
220 values.append( (l, str(i)) )
221 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
222 yield (typ, name.strip(), values, comment)
226 def parse_include(self, name):
227 """Parse include file.
229 This generates a tuple for each function:
230 (return_type, method_name, parameter_list, comment)
231 with parameter_list being a list of tuples (parameter_type, parameter_name).
237 # Note: lstrip() should not be necessary, but there is 1 badly
238 # formatted comment in vlc1.0.0 includes
239 if l.lstrip().startswith('/**'):
242 elif l.startswith(' * '):
243 comment = comment + l[3:]
249 accumulator=" ".join( (accumulator, l) )
254 elif l.startswith('VLC_PUBLIC_API') and not l.endswith(');'):
255 # Multiline definition. Accumulate until end of definition
261 (ret, param)=m.groups()
263 rtype, method=self.parse_param(ret)
266 for p in paramlist_re.split(param):
267 params.append( self.parse_param(p) )
269 if len(params) == 1 and params[0][0] == 'void':
270 # Empty parameter list
273 if list(p for p in params if not p[1]):
274 # Empty parameter names. Have to poke into comment.
275 names=comment_re.findall(comment)
276 if len(names) < len(params):
277 # Bad description: all parameters are not specified.
278 # Generate default parameter names
279 badnames=[ "param%d" % i for i in xrange(len(params)) ]
280 # Put in the existing ones
281 for (i, p) in enumerate(names):
284 print "### Error ###"
285 print "### Cannot get parameter names from comment for %s: %s" % (method, comment.replace("\n", ' '))
286 # Note: this was previously
287 # raise Exception("Cannot get parameter names from comment for %s: %s" % (method, comment))
288 # but it prevented code generation for a minor detail (some bad descriptions).
289 params=[ (p[0], names[i]) for (i, p) in enumerate(params) ]
291 # Transform Doxygen syntax into epydoc syntax
292 comment=comment.replace('\\param', '@param').replace('\\return', '@return')
295 print '********************'
298 print "%s (%s)" % (method, rtype)
299 for typ, name in params:
300 print " %s (%s)" % (name, typ)
301 print '********************'
308 def dump_methods(self):
309 print "** Defined functions **"
310 for (rtype, name, params, comment) in self.methods:
311 print "%(name)s (%(rtype)s):" % locals()
313 print " %(n)s (%(t)s)" % locals()
315 def dump_enums(self):
316 print "** Defined enums **"
317 for (typ, name, values, comment) in self.enums:
318 print "%(name)s (%(typ)s):" % locals()
320 print " %(k)s=%(v)s" % locals()
322 class PythonGenerator(object):
323 # C-type to ctypes/python type conversion.
324 # Note that enum types conversions are generated (cf convert_enum_names)
326 'libvlc_exception_t*': 'ctypes.POINTER(VLCException)',
328 'libvlc_media_player_t*': 'MediaPlayer',
329 'libvlc_instance_t*': 'Instance',
330 'libvlc_media_t*': 'Media',
331 'libvlc_log_t*': 'Log',
332 'libvlc_log_iterator_t*': 'LogIterator',
333 'libvlc_log_message_t*': 'ctypes.POINTER(LogMessage)',
334 'libvlc_event_type_t': 'ctypes.c_uint',
335 'libvlc_event_manager_t*': 'EventManager',
336 'libvlc_media_discoverer_t*': 'MediaDiscoverer',
337 'libvlc_media_library_t*': 'MediaLibrary',
338 'libvlc_media_list_t*': 'MediaList',
339 'libvlc_media_list_player_t*': 'MediaListPlayer',
340 'libvlc_media_list_view_t*': 'MediaListView',
341 'libvlc_track_description_t*': 'TrackDescription',
342 'libvlc_audio_output_t*': 'AudioOutput',
343 'libvlc_media_stats_t*': 'ctypes.POINTER(MediaStats)',
345 'mediacontrol_Instance*': 'MediaControl',
346 'mediacontrol_Exception*': 'MediaControlException',
347 'mediacontrol_RGBPicture*': 'ctypes.POINTER(RGBPicture)',
348 'mediacontrol_PlaylistSeq*': 'MediaControlPlaylistSeq',
349 'mediacontrol_Position*': 'ctypes.POINTER(MediaControlPosition)',
350 'mediacontrol_StreamInformation*': 'ctypes.POINTER(MediaControlStreamInformation)',
351 'WINDOWHANDLE': 'ctypes.c_ulong',
354 'void*': 'ctypes.c_void_p',
355 'short': 'ctypes.c_short',
356 'char*': 'ctypes.c_char_p',
357 'char**': 'ListPOINTER(ctypes.c_char_p)',
358 'uint32_t': 'ctypes.c_uint',
359 'float': 'ctypes.c_float',
360 'unsigned': 'ctypes.c_uint',
361 'int': 'ctypes.c_int',
362 '...': 'FIXMEva_list',
363 'libvlc_callback_t': 'ctypes.c_void_p',
364 'libvlc_time_t': 'ctypes.c_longlong',
367 # Defined python classes, i.e. classes for which we want to generate
368 # class wrappers around libvlc functions
386 def __init__(self, parser=None):
389 # Generate python names for enums
390 self.type2class.update(self.convert_enum_names(parser.enums))
393 # Definition of prefixes that we can strip from method names when
394 # wrapping them into class methods
395 self.prefixes=dict( (v, k[:-2])
396 for (k, v) in self.type2class.iteritems()
397 if v in self.defined_classes )
398 self.prefixes['MediaControl']='mediacontrol_'
400 def save(self, filename=None):
401 if filename is None or filename == '-':
404 self.fd=open(filename, 'w')
406 self.insert_code('header.py')
407 wrapped_methods=self.generate_wrappers(self.parser.methods)
408 for l in self.parser.methods:
409 self.output_ctypes(*l)
410 self.insert_code('footer.py')
412 all_methods=set( t[1] for t in self.parser.methods )
413 not_wrapped=all_methods.difference(wrapped_methods)
414 self.output("# Not wrapped methods:")
415 for m in not_wrapped:
418 if self.fd != sys.stdout:
421 def output(self, *p):
422 self.fd.write(" ".join(p))
425 def check_types(self):
426 """Make sure that all types are properly translated.
428 This method must be called *after* convert_enum_names, since
429 the latter populates type2class with converted enum names.
431 for (rt, met, params, c) in self.parser.methods:
432 for typ, name in params:
433 if not typ in self.type2class:
434 raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
436 def insert_code(self, filename):
437 """Generate header/footer code.
439 f=open(filename, 'r')
441 if l.startswith('build_date'):
442 self.output('build_date="%s"' % time.ctime())
443 elif l.startswith('# GENERATED_ENUMS'):
444 self.generate_enums(self.parser.enums)
446 self.output(l.rstrip())
450 def convert_enum_names(self, enums):
452 for (typ, name, values, comment) in enums:
454 raise Exception('This method only handles enums')
455 pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
457 pyname=pyname.title().replace('_', '')
458 elif not pyname[0].isupper():
459 pyname=pyname.capitalize()
463 def generate_enums(self, enums):
464 for (typ, name, values, comment) in enums:
466 raise Exception('This method only handles enums')
467 pyname=self.type2class[name]
469 self.output("class %s(ctypes.c_ulong):" % pyname)
470 self.output(' """%s\n """' % comment)
473 # Convert symbol names
477 # Single character. Some symbols use 1_1, 5_1, etc.
478 n="_".join( k.split('_')[-2:] )
479 if re.match('^[0-9]', n):
480 # Cannot start an identifier with a number
484 self.output(" _names={")
486 self.output(" %s: '%s'," % (v, conv[k]))
491 return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
493 def __eq__(self, other):
494 return ( (isinstance(other, ctypes.c_ulong) and self.value == other.value)
495 or (isinstance(other, (int, long)) and self.value == other ) )
497 def __ne__(self, other):
498 return not self.__eq__(other)
501 self.output("%(class)s.%(attribute)s=%(class)s(%(value)s)" % {
503 'attribute': conv[k],
508 def output_ctypes(self, rtype, method, params, comment):
509 """Output ctypes decorator for the given method.
511 if method in blacklist:
515 self.output("""if hasattr(dll, '%s'):""" % method)
517 self.output(" prototype=ctypes.CFUNCTYPE(%s, %s)" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
518 ", ".join( self.type2class[p[0]] for p in params )))
520 self.output(" prototype=ctypes.CFUNCTYPE(%s)" % self.type2class.get(rtype, 'FIXME_%s' % rtype))
524 flags=' paramflags= tuple()'
525 elif len(params) == 1:
526 flags=" paramflags=( (%d, ), )" % parameter_passing[params[0][0]]
528 flags=" paramflags=%s" % ", ".join( '(%d,)' % parameter_passing[p[0]] for p in params )
530 self.output(' %s = prototype( ("%s", dll), paramflags )' % (method, method))
532 # A VLCException is present. Process it.
533 self.output(" %s.errcheck = check_vlc_exception" % method)
534 self.output(' %s.__doc__ = """%s"""' % (method, comment))
537 def parse_override(self, name):
538 """Parse override definitions file.
540 It is possible to override methods definitions in classes.
543 (code, overriden_methods, docstring)
551 m=re.match('class (\S+):', l)
554 if current is not None:
555 code[current]="".join(data)
560 code[current]="".join(data)
564 for k, v in code.iteritems():
565 if v.lstrip().startswith('"""'):
566 # Starting comment. Use it as docstring.
567 dummy, docstring[k], code[k]=v.split('"""', 2)
569 # Not robust wrt. internal methods, but this works for the moment.
570 overridden_methods=dict( (k, re.findall('^\s+def\s+(\w+)', v, re.MULTILINE)) for (k, v) in code.iteritems() )
572 return code, overridden_methods, docstring
574 def fix_python_comment(self, c):
575 """Fix comment by removing first and last parameters (self and exception)
577 data=c.replace('@{', '').replace('@see', 'See').splitlines()
578 body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
579 param=[ python_param_re.sub('\\1:\\2', l) for l in itertools.ifilter(lambda l: '@param' in l, data) ]
580 ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
584 elif len(param) == 1:
587 return "\n".join(itertools.chain(body, param, ret))
589 def generate_wrappers(self, methods):
590 """Generate class wrappers for all appropriate methods.
592 @return: the set of wrapped method names
595 # Sort methods against the element they apply to.
596 elements=sorted( ( (self.type2class.get(params[0][0]), rt, met, params, c)
597 for (rt, met, params, c) in methods
598 if params and self.type2class.get(params[0][0], '_') in self.defined_classes
600 key=operator.itemgetter(0))
602 overrides, overriden_methods, docstring=self.parse_override('override.py')
604 for classname, el in itertools.groupby(elements, key=operator.itemgetter(0)):
605 self.output("""class %(name)s(object):""" % {'name': classname})
606 if classname in docstring:
607 self.output(' """%s\n """' % docstring[classname])
609 if not 'def __new__' in overrides.get(classname, ''):
611 def __new__(cls, pointer=None):
612 '''Internal method used for instanciating wrappers from ctypes.
615 raise Exception("Internal method. Surely this class cannot be instanciated by itself.")
619 o=object.__new__(cls)
620 o._as_parameter_=ctypes.c_void_p(pointer)
627 '''(INTERNAL) ctypes parameter conversion method.
629 return arg._as_parameter_
632 if classname in overrides:
633 self.output(overrides[classname])
635 prefix=self.prefixes.get(classname, '')
637 for cl, rtype, method, params, comment in el:
638 if method in blacklist:
641 name=method.replace(prefix, '').replace('libvlc_', '')
643 if name in overriden_methods.get(cl, []):
644 # Method already defined in override.py
648 params[0]=(params[0][0], 'self')
649 if params and params[-1][0] in ('libvlc_exception_t*', 'mediacontrol_Exception*'):
650 args=", ".join( p[1] for p in params[:-1] )
652 args=", ".join( p[1] for p in params )
654 self.output(" if hasattr(dll, '%s'):" % method)
655 self.output(" def %s(%s):" % (name, args))
656 self.output(' """%s\n """' % self.fix_python_comment(comment))
657 if params and params[-1][0] == 'libvlc_exception_t*':
659 self.output(" e=VLCException()")
660 self.output(" return %s(%s, e)" % (method, args))
661 elif params and params[-1][0] == 'mediacontrol_Exception*':
663 self.output(" e=MediaControlException()")
664 self.output(" return %s(%s, e)" % (method, args))
666 self.output(" return %s(%s)" % (method, args))
669 # Check for standard methods
671 # There is a count method. Generate a __len__ one.
672 if params and params[-1][0] == 'libvlc_exception_t*':
673 self.output(""" def __len__(self):
679 self.output(""" def __len__(self):
682 elif name.endswith('item_at_index'):
683 # Indexable (and thus iterable)"
684 self.output(""" def __getitem__(self, i):
686 return %s(self, i, e)
690 for i in xrange(len(self)):
695 class JavaGenerator(object):
696 # C-type to java/jna type conversion.
697 # Note that enum types conversions are generated (cf convert_enum_names)
699 'libvlc_exception_t*': 'libvlc_exception_t',
700 'libvlc_media_player_t*': 'LibVlcMediaPlayer',
701 'libvlc_instance_t*': 'LibVlcInstance',
702 'libvlc_media_t*': 'LibVlcMedia',
703 'libvlc_log_t*': 'LibVlcLog',
704 'libvlc_log_iterator_t*': 'LibVlcLogIterator',
705 'libvlc_log_message_t*': 'libvlc_log_message_t',
706 'libvlc_event_type_t': 'int',
707 'libvlc_event_manager_t*': 'LibVlcEventManager',
708 'libvlc_media_discoverer_t*': 'LibVlcMediaDiscoverer',
709 'libvlc_media_library_t*': 'LibVlcMediaLibrary',
710 'libvlc_media_list_t*': 'LibVlcMediaList',
711 'libvlc_media_list_player_t*': 'LibVlcMediaListPlayer',
712 'libvlc_media_list_view_t*': 'LibVlcMediaListView',
713 'libvlc_media_stats_t*': 'LibVlcMediaStats',
715 'libvlc_track_description_t*': 'LibVlcTrackDescription',
716 'libvlc_audio_output_t*': 'LibVlcAudioOutput',
722 'char**': 'String[]',
723 'uint32_t': 'uint32',
727 '...': 'FIXMEva_list',
728 'libvlc_callback_t': 'LibVlcCallback',
729 'libvlc_time_t': 'long',
731 'mediacontrol_RGBPicture*': 'Pointer',
732 'mediacontrol_PlaylistSeq*': 'Pointer',
733 'mediacontrol_StreamInformation*': 'Pointer',
736 def __init__(self, parser=None):
739 # Blacklist all mediacontrol methods
740 for (rt, met, params, c) in self.parser.methods:
741 if met.startswith('mediacontrol'):
742 blacklist.append(met)
743 # Generate Java names for enums
744 self.type2class.update(self.convert_enum_names(parser.enums))
747 def save(self, dirname=None):
748 if dirname is None or dirname == '-':
750 if not os.path.isdir(dirname):
753 print "Generating java code in %s/" % dirname
755 # Generate enum files
756 self.generate_enums(dirname, self.parser.enums)
758 # Generate LibVlc.java code
759 self.generate_libvlc(dirname)
761 def output(self, fd, *p):
762 fd.write(" ".join(p))
765 def check_types(self):
766 """Make sure that all types are properly translated.
768 This method must be called *after* convert_enum_names, since
769 the latter populates type2class with converted enum names.
771 for (rt, met, params, c) in self.parser.methods:
774 for typ, name in params:
775 if not typ in self.type2class:
776 raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
778 def convert_enum_names(self, enums):
779 """Convert enum names into Java names.
782 for (typ, name, values, comment) in enums:
784 raise Exception('This method only handles enums')
785 pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
787 pyname=pyname.title().replace('_', '')
788 elif not pyname[0].isupper():
789 pyname=pyname.capitalize()
793 def insert_code(self, fd, filename):
794 """Generate header/footer code.
796 f=open(filename, 'r')
798 if l.startswith('build_date'):
799 self.output(fd, 'build_date="%s";' % time.ctime())
801 self.output(fd, l.rstrip())
804 def generate_header(self, fd):
805 """Generate LibVlc header.
807 for (c_type, jna_type) in self.type2class.iteritems():
808 if c_type.endswith('*') and jna_type.startswith('LibVlc'):
809 self.output(fd, ''' public class %s extends PointerType
814 def generate_libvlc(self, dirname):
815 """Generate LibVlc.java JNA glue code.
817 filename=os.path.join(dirname, 'LibVlc.java')
818 fd=open(filename, 'w')
820 self.insert_code(fd, 'boilerplate.java')
821 self.insert_code(fd, 'LibVlc-header.java')
822 #wrapped_methods=self.generate_wrappers(self.parser.methods)
823 self.generate_header(fd)
824 for (rtype, method, params, comment) in self.parser.methods:
825 if method in blacklist:
828 self.output(fd, "%s %s(%s);\n" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
830 ", ".join( ("%s %s" % (self.type2class[p[0]],
831 p[1])) for p in params )))
832 self.insert_code(fd, 'LibVlc-footer.java')
835 def generate_enums(self, dirname, enums):
836 """Generate JNA glue code for enums
838 for (typ, name, values, comment) in enums:
840 raise Exception('This method only handles enums')
841 javaname=self.type2class[name]
843 filename=javaname+".java"
845 fd=open(os.path.join(dirname, filename), 'w')
847 self.insert_code(fd, 'boilerplate.java')
848 self.output(fd, """package org.videolan.jvlc.internal;
854 # FIXME: write comment
857 self.output(fd, " %s (%s)," % (k, v))
859 self.output(fd, " private final int _value;");
860 self.output(fd, " %s(int value) { this._value = value; }" % javaname);
861 self.output(fd, " public int value() { return this._value; }");
865 def fix_python_comment(self, c):
866 """Fix comment by removing first and last parameters (self and exception)
868 data=c.replace('@{', '').replace('@see', 'See').splitlines()
869 body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
870 param=[ python_param_re.sub('\\1:\\2', l) for l in itertools.ifilter(lambda l: '@param' in l, data) ]
871 ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
875 elif len(param) == 1:
878 return "\n".join(itertools.chain(body, param, ret))
880 def process(output, list_of_includes):
881 p=Parser(list_of_includes)
885 if __name__ == '__main__':
886 opt=OptionParser(usage="""Parse VLC include files and generate bindings code.
887 %prog [options] include_file.h [...]""")
889 opt.add_option("-d", "--debug", dest="debug", action="store_true",
893 opt.add_option("-c", "--check", dest="check", action="store_true",
897 opt.add_option("-j", "--java", dest="java", action="store_true",
899 help="Generate java bindings (default is python)")
901 opt.add_option("-o", "--output", dest="output", action="store",
902 type="str", default="-",
903 help="Output filename(python)/dirname(java)")
905 (options, args) = opt.parse_args()
913 # Various consistency checks.
914 for (rt, name, params, comment) in p.methods:
915 if not comment.strip():
916 print "No comment for %s" % name
918 names=comment_re.findall(comment)
919 if len(names) != len(params):
920 print "Docstring comment parameters mismatch for %s" % name
926 if options.check or options.debug:
934 g.save(options.output)