]> git.sesse.net Git - vlc/blob - bindings/python-ctypes/generate.py
f1fdf0567bb69c7a018969ad6111dc6d0bca66f9
[vlc] / bindings / python-ctypes / generate.py
1 #! /usr/bin/python
2 debug=False
3
4 #
5 # Code generator for python ctypes bindings for VLC
6 # Copyright (C) 2009 the VideoLAN team
7 # $Id: $
8 #
9 # Authors: Olivier Aubert <olivier.aubert at liris.cnrs.fr>
10 #
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.
15 #
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.
20 #
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.
24 #
25
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.
29 """
30
31 import sys
32 import re
33 import time
34 import operator
35 import itertools
36 from optparse import OptionParser
37
38 # DefaultDict from ASPN python cookbook
39 import copy
40 class DefaultDict(dict):
41     """Dictionary with a default value for unknown keys."""
42     def __init__(self, default=None, **items):
43         dict.__init__(self, **items)
44         self.default = default
45
46     def __getitem__(self, key):
47         if key in self:
48             return self.get(key)
49         else:
50             ## Need copy in case self.default is something like []
51             return self.setdefault(key, copy.deepcopy(self.default))
52
53     def __copy__(self):
54         return DefaultDict(self.default, **self)
55
56 # Methods not decorated/not referenced
57 blacklist=[
58     "libvlc_exception_raise",
59     "libvlc_exception_raised",
60     "libvlc_exception_get_message",
61     "libvlc_get_vlc_instance",
62
63     "libvlc_media_list_view_index_of_item",
64     "libvlc_media_list_view_insert_at_index",
65     "libvlc_media_list_view_remove_at_index",
66     "libvlc_media_list_view_add_item",
67
68     # In svn but not in current 1.0.0.
69     #"libvlc_media_add_option_flag",
70     #'libvlc_video_set_deinterlace',
71     #'libvlc_video_get_marquee_option_as_int',
72     #'libvlc_video_get_marquee_option_as_string',
73     #'libvlc_video_set_marquee_option_as_int',
74     #'libvlc_video_set_marquee_option_as_string',
75     #'libvlc_vlm_get_event_manager',
76     #"libvlc_media_list_player_event_manager",
77     #'libvlc_media_player_next_frame',
78
79     'mediacontrol_PlaylistSeq__free',
80     ]
81
82 # Precompiled regexps
83 api_re=re.compile('VLC_PUBLIC_API\s+(\S+\s+.+?)\s*\(\s*(.+?)\s*\)')
84 param_re=re.compile('\s*(const\s*|unsigned\s*|struct\s*)?(\S+\s*\**)\s+(.+)')
85 paramlist_re=re.compile('\s*,\s*')
86 comment_re=re.compile('\\param\s+(\S+)')
87 python_param_re=re.compile('(@param\s+\S+)(.+)')
88 forward_re=re.compile('.+\(\s*(.+?)\s*\)(\s*\S+)')
89 enum_re=re.compile('typedef\s+(enum)\s*(\S+\s*)?\{\s*(.+)\s*\}\s*(\S+);')
90 special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
91 event_def_re=re.compile('^DEF\(\s*(\w+)\s*\)')
92
93 # Definition of parameter passing mode for types.  This should not be
94 # hardcoded this way, but works alright ATM.
95 parameter_passing=DefaultDict(default=1)
96 parameter_passing['libvlc_exception_t*']=3
97
98 class Parser(object):
99     def __init__(self, list_of_files):
100         self.methods=[]
101         self.enums=[]
102
103         for name in list_of_files:
104             self.enums.extend(self.parse_typedef(name))
105             self.methods.extend(self.parse_include(name))
106
107     def parse_param(self, s):
108         """Parse a C parameter expression.
109
110         It is used to parse both the type/name for methods, and type/name
111         for their parameters.
112
113         It returns a tuple (type, name).
114         """
115         s=s.strip()
116         s=s.replace('const', '')
117         if 'VLC_FORWARD' in s:
118             m=forward_re.match(s)
119             s=m.group(1)+m.group(2)
120         m=param_re.search(s)
121         if m:
122             const, typ, name=m.groups()
123             while name.startswith('*'):
124                 typ += '*'
125                 name=name[1:]
126             if name == 'const*':
127                 # K&R definition: const char * const*
128                 name=''
129             typ=typ.replace(' ', '')
130             return typ, name
131         else:
132             # K&R definition: only type
133             return s.replace(' ', ''), ''
134
135     def parse_typedef(self, name):
136         """Parse include file for typedef expressions.
137
138         This generates a tuple for each typedef:
139         (type, name, value_list, comment)
140         with type == 'enum' (for the moment) and value_list being a list of (name, value)
141         Note that values are string, since this is intended for code generation.
142         """
143         event_names=[]
144
145         f=open(name, 'r')
146         accumulator=''
147         for l in f:
148             # Note: lstrip() should not be necessary, but there is 1 badly
149             # formatted comment in vlc1.0.0 includes
150             if l.lstrip().startswith('/**'):
151                 comment=''
152                 continue
153             elif l.startswith(' * '):
154                 comment = comment + l[3:]
155                 continue
156
157             l=l.strip()
158             if l.startswith('/*') or l.endswith('*/'):
159                 continue
160
161             if (l.startswith('typedef enum') or l.startswith('enum')) and not l.endswith(';'):
162                 # Multiline definition. Accumulate until end of definition
163                 accumulator=l
164                 continue
165             elif accumulator:
166                 accumulator=" ".join( (accumulator, l) )
167                 if l.endswith(';'):
168                     # End of definition
169                     l=accumulator
170                     accumulator=''
171
172             m=enum_re.match(l)
173             if m:
174                 values=[]
175                 (typ, dummy, data, name)=m.groups()
176                 for i, l in enumerate(paramlist_re.split(data)):
177                     l=l.strip()
178                     if l.startswith('/*'):
179                         continue
180                     if '=' in l:
181                         # A value was specified. Use it.
182                         values.append(re.split('\s*=\s*', l))
183                     else:
184                         if l:
185                             values.append( (l, str(i)) )
186                 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
187                 yield (typ, name.strip(), values, comment)
188                 comment=''
189                 continue
190
191             # Special case, used only for libvlc_events.h
192             # (version after 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
193             m=event_def_re.match(l)
194             if m:
195                 # Event definition.
196                 event_names.append('libvlc_'+m.group(1))
197                 continue
198
199             # Special case, used only for libvlc_events.h
200             m=special_enum_re.match(l)
201             if m:
202                 (typ, name, data)=m.groups()
203                 if event_names:
204                     # event_names were defined through DEF macro
205                     # (see 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
206                     values=list( (n, str(i)) for i, n in enumerate(event_names))
207                 else:
208                     # Before 96a96f60bb0d1f2506e68b356897ceca6f6b586d
209                     values=[]
210                     for i, l in enumerate(paramlist_re.split(data)):
211                         l=l.strip()
212                         if l.startswith('/*') or l.startswith('#'):
213                             continue
214                         if '=' in l:
215                             # A value was specified. Use it.
216                             values.append(re.split('\s*=\s*', l))
217                         else:
218                             if l:
219                                 values.append( (l, str(i)) )
220                 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
221                 yield (typ, name.strip(), values, comment)
222                 comment=''
223                 continue
224
225     def parse_include(self, name):
226         """Parse include file.
227
228         This generates a tuple for each function:
229         (return_type, method_name, parameter_list, comment)
230         with parameter_list being a list of tuples (parameter_type, parameter_name).
231         """
232         f=open(name, 'r')
233         accumulator=''
234         comment=''
235         for l in f:
236             # Note: lstrip() should not be necessary, but there is 1 badly
237             # formatted comment in vlc1.0.0 includes
238             if l.lstrip().startswith('/**'):
239                 comment=''
240                 continue
241             elif l.startswith(' * '):
242                 comment = comment + l[3:]
243                 continue
244
245             l=l.strip()
246
247             if accumulator:
248                 accumulator=" ".join( (accumulator, l) )
249                 if l.endswith(');'):
250                     # End of definition
251                     l=accumulator
252                     accumulator=''
253             elif l.startswith('VLC_PUBLIC_API') and not l.endswith(');'):
254                 # Multiline definition. Accumulate until end of definition
255                 accumulator=l
256                 continue
257
258             m=api_re.match(l)
259             if m:
260                 (ret, param)=m.groups()
261
262                 rtype, method=self.parse_param(ret)
263
264                 params=[]
265                 for p in paramlist_re.split(param):
266                     params.append( self.parse_param(p) )
267
268                 if len(params) == 1 and params[0][0] == 'void':
269                     # Empty parameter list
270                     params=[]
271
272                 if list(p for p in params if not p[1]):
273                     # Empty parameter names. Have to poke into comment.
274                     names=comment_re.findall(comment)
275                     if len(names) < len(params):
276                         # Bad description: all parameters are not specified.
277                         # Generate default parameter names
278                         badnames=[ "param%d" % i for i in xrange(len(params)) ]
279                         # Put in the existing ones
280                         for (i, p) in enumerate(names):
281                             badnames[i]=names[i]
282                         names=badnames
283                         print "### Error ###"
284                         print "### Cannot get parameter names from comment for %s: %s" % (method, comment.replace("\n", ' '))
285                         # Note: this was previously
286                         # raise Exception("Cannot get parameter names from comment for %s: %s" % (method, comment))
287                         # but it prevented code generation for a minor detail (some bad descriptions).
288                     params=[ (p[0], names[i]) for (i, p) in enumerate(params) ]
289
290                 # Transform Doxygen syntax into epydoc syntax
291                 comment=comment.replace('\\param', '@param').replace('\\return', '@return')
292
293                 if debug:
294                     print '********************'
295                     print l
296                     print '-------->'
297                     print "%s (%s)" % (method, rtype)
298                     for typ, name in params:
299                         print "        %s (%s)" % (name, typ)
300                     print '********************'
301                 yield (rtype,
302                        method,
303                        params,
304                        comment)
305                 comment=''
306
307     def dump_methods(self):
308         print "** Defined functions **"
309         for (rtype, name, params, comment) in self.methods:
310             print "%(name)s (%(rtype)s):" % locals()
311             for t, n in params:
312                 print "    %(n)s (%(t)s)" % locals()
313
314     def dump_enums(self):
315         print "** Defined enums **"
316         for (typ, name, values, comment) in self.enums:
317             print "%(name)s (%(typ)s):" % locals()
318             for k, v in values:
319                 print "    %(k)s=%(v)s" % locals()
320
321 class PythonGenerator(object):
322     # C-type to ctypes/python type conversion.
323     # Note that enum types conversions are generated (cf convert_enum_names)
324     type2class={
325         'libvlc_exception_t*': 'ctypes.POINTER(VLCException)',
326
327         'libvlc_media_player_t*': 'MediaPlayer',
328         'libvlc_instance_t*': 'Instance',
329         'libvlc_media_t*': 'Media',
330         'libvlc_log_t*': 'Log',
331         'libvlc_log_iterator_t*': 'LogIterator',
332         'libvlc_log_message_t*': 'ctypes.POINTER(LogMessage)',
333         'libvlc_event_type_t': 'ctypes.c_uint',
334         'libvlc_event_manager_t*': 'EventManager',
335         'libvlc_media_discoverer_t*': 'MediaDiscoverer',
336         'libvlc_media_library_t*': 'MediaLibrary',
337         'libvlc_media_list_t*': 'MediaList',
338         'libvlc_media_list_player_t*': 'MediaListPlayer',
339         'libvlc_media_list_view_t*': 'MediaListView',
340         'libvlc_track_description_t*': 'TrackDescription',
341         'libvlc_audio_output_t*': 'AudioOutput',
342
343         'mediacontrol_Instance*': 'MediaControl',
344         'mediacontrol_Exception*': 'MediaControlException',
345         'mediacontrol_RGBPicture*': 'ctypes.POINTER(RGBPicture)',
346         'mediacontrol_PlaylistSeq*': 'MediaControlPlaylistSeq',
347         'mediacontrol_Position*': 'ctypes.POINTER(MediaControlPosition)',
348         'mediacontrol_StreamInformation*': 'ctypes.POINTER(MediaControlStreamInformation)',
349         'WINDOWHANDLE': 'ctypes.c_ulong',
350
351         'void': 'None',
352         'void*': 'ctypes.c_void_p',
353         'short': 'ctypes.c_short',
354         'char*': 'ctypes.c_char_p',
355         'char**': 'ListPOINTER(ctypes.c_char_p)',
356         'uint32_t': 'ctypes.c_uint',
357         'float': 'ctypes.c_float',
358         'unsigned': 'ctypes.c_uint',
359         'int': 'ctypes.c_int',
360         '...': 'FIXMEva_list',
361         'libvlc_callback_t': 'ctypes.c_void_p',
362         'libvlc_time_t': 'ctypes.c_longlong',
363         }
364
365     # Defined python classes, i.e. classes for which we want to generate
366     # class wrappers around libvlc functions
367     defined_classes=(
368         'MediaPlayer',
369         'Instance',
370         'Media',
371         'Log',
372         'LogIterator',
373         'EventManager',
374         'MediaDiscoverer',
375         'MediaLibrary',
376         'MediaList',
377         'MediaListPlayer',
378         'MediaListView',
379         'TrackDescription',
380         'AudioOutput',
381         'MediaControl',
382         )
383
384     def __init__(self, parser=None):
385         self.parser=parser
386
387         # Generate python names for enums
388         self.type2class.update(self.convert_enum_names(parser.enums))
389         self.check_types()
390
391         # Definition of prefixes that we can strip from method names when
392         # wrapping them into class methods
393         self.prefixes=dict( (v, k[:-2])
394                             for (k, v) in self.type2class.iteritems()
395                             if  v in self.defined_classes )
396         self.prefixes['MediaControl']='mediacontrol_'
397
398     def save(self, filename=None):
399         if filename is None or filename == '-':
400             self.fd=sys.stdout
401         else:
402             self.fd=open(filename, 'w')
403
404         self.insert_code('header.py')
405         wrapped_methods=self.generate_wrappers(self.parser.methods)
406         for l in self.parser.methods:
407             self.output_ctypes(*l)
408         self.insert_code('footer.py')
409
410         all_methods=set( t[1] for t in self.parser.methods )
411         not_wrapped=all_methods.difference(wrapped_methods)
412         self.output("# Not wrapped methods:")
413         for m in not_wrapped:
414             self.output("#   ", m)
415
416         if self.fd != sys.stdout:
417             self.fd.close()
418
419     def output(self, *p):
420         self.fd.write(" ".join(p))
421         self.fd.write("\n")
422
423     def check_types(self):
424         """Make sure that all types are properly translated.
425
426         This method must be called *after* convert_enum_names, since
427         the latter populates type2class with converted enum names.
428         """
429         for (rt, met, params, c) in self.parser.methods:
430             for typ, name in params:
431                 if not typ in self.type2class:
432                     raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
433
434     def insert_code(self, filename):
435         """Generate header/footer code.
436         """
437         f=open(filename, 'r')
438         for l in f:
439             if l.startswith('build_date'):
440                 self.output('build_date="%s"' % time.ctime())
441             elif l.startswith('# GENERATED_ENUMS'):
442                 self.generate_enums(self.parser.enums)
443             else:
444                 self.output(l.rstrip())
445
446         f.close()
447
448     def convert_enum_names(self, enums):
449         res={}
450         for (typ, name, values, comment) in enums:
451             if typ != 'enum':
452                 raise Exception('This method only handles enums')
453             pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
454             if '_' in pyname:
455                 pyname=pyname.title().replace('_', '')
456             elif not pyname[0].isupper():
457                 pyname=pyname.capitalize()
458             res[name]=pyname
459         return res
460
461     def generate_enums(self, enums):
462         for (typ, name, values, comment) in enums:
463             if typ != 'enum':
464                 raise Exception('This method only handles enums')
465             pyname=self.type2class[name]
466
467             self.output("class %s(ctypes.c_ulong):" % pyname)
468             self.output('    """%s\n    """' % comment)
469
470             conv={}
471             # Convert symbol names
472             for k, v in values:
473                 n=k.split('_')[-1]
474                 if len(n) == 1:
475                     # Single character. Some symbols use 1_1, 5_1, etc.
476                     n="_".join( k.split('_')[-2:] )
477                 if re.match('^[0-9]', n):
478                     # Cannot start an identifier with a number
479                     n='_'+n
480                 conv[k]=n
481
482             self.output("    _names={")
483             for k, v in values:
484                 self.output("        %s: '%s'," % (v, conv[k]))
485             self.output("    }")
486
487             self.output("""
488     def __repr__(self):
489         return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
490
491     def __eq__(self, other):
492         return ( (isinstance(other, ctypes.c_ulong) and self.value == other.value)
493                  or (isinstance(other, (int, long)) and self.value == other ) )
494
495     def __ne__(self, other):
496         return not self.__eq__(other)
497     """)
498             for k, v in values:
499                 self.output("%(class)s.%(attribute)s=%(class)s(%(value)s)" % {
500                         'class': pyname,
501                         'attribute': conv[k],
502                         'value': v
503                         })
504             self.output("")
505
506     def output_ctypes(self, rtype, method, params, comment):
507         """Output ctypes decorator for the given method.
508         """
509         if method in blacklist:
510             # FIXME
511             return
512
513         self.output("""if hasattr(dll, '%s'):""" % method)
514         if params:
515             self.output("    prototype=ctypes.CFUNCTYPE(%s, %s)" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
516                                                                 ",".join( self.type2class[p[0]] for p in params )))
517         else:
518             self.output("    prototype=ctypes.CFUNCTYPE(%s)" % self.type2class.get(rtype, 'FIXME_%s' % rtype))
519
520
521         if not params:
522             flags='    paramflags= tuple()'
523         elif len(params) == 1:
524             flags="    paramflags=( (%d, ), )" % parameter_passing[params[0][0]]
525         else:
526             flags="    paramflags=%s" % ",".join( '(%d,)' % parameter_passing[p[0]] for p in params )
527         self.output(flags)
528         self.output('    %s = prototype( ("%s", dll), paramflags )' % (method, method))
529         if '3' in flags:
530             # A VLCException is present. Process it.
531             self.output("    %s.errcheck = check_vlc_exception" % method)
532         self.output('    %s.__doc__ = """%s"""' % (method, comment))
533         self.output()
534
535     def parse_override(self, name):
536         """Parse override definitions file.
537
538         It is possible to override methods definitions in classes.
539
540         It returns a tuple
541         (code, overriden_methods, docstring)
542         """
543         code={}
544
545         data=[]
546         current=None
547         f=open(name, 'r')
548         for l in f:
549             m=re.match('class (\S+):', l)
550             if m:
551                 # Dump old data
552                 if current is not None:
553                     code[current]="".join(data)
554                 current=m.group(1)
555                 data=[]
556                 continue
557             data.append(l)
558         code[current]="".join(data)
559         f.close()
560
561         docstring={}
562         for k, v in code.iteritems():
563             if v.lstrip().startswith('"""'):
564                 # Starting comment. Use it as docstring.
565                 dummy, docstring[k], code[k]=v.split('"""', 2)
566
567         # Not robust wrt. internal methods, but this works for the moment.
568         overridden_methods=dict( (k, re.findall('^\s+def\s+(\w+)', v, re.MULTILINE)) for (k, v) in code.iteritems() )
569
570         return code, overridden_methods, docstring
571
572     def fix_python_comment(self, c):
573         """Fix comment by removing first and last parameters (self and exception)
574         """
575         data=c.replace('@{', '').replace('@see', 'See').splitlines()
576         body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
577         param=[ python_param_re.sub('\\1:\\2', l) for l in  itertools.ifilter(lambda l: '@param' in l, data) ]
578         ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
579
580         if len(param) >= 2:
581             param=param[1:-1]
582         elif len(param) == 1:
583             param=[]
584
585         return "\n".join(itertools.chain(body, param, ret))
586
587     def generate_wrappers(self, methods):
588         """Generate class wrappers for all appropriate methods.
589
590         @return: the set of wrapped method names
591         """
592         ret=set()
593         # Sort methods against the element they apply to.
594         elements=sorted( ( (self.type2class.get(params[0][0]), rt, met, params, c)
595                            for (rt, met, params, c) in methods
596                            if params and self.type2class.get(params[0][0], '_') in self.defined_classes
597                            ),
598                          key=operator.itemgetter(0))
599
600         overrides, overriden_methods, docstring=self.parse_override('override.py')
601
602         for classname, el in itertools.groupby(elements, key=operator.itemgetter(0)):
603             self.output("""class %(name)s(object):""" % {'name': classname})
604             if classname in docstring:
605                 self.output('    """%s\n    """' % docstring[classname])
606
607             self.output("""
608     def __new__(cls, pointer=None):
609         '''Internal method used for instanciating wrappers from ctypes.
610         '''
611         if pointer is None:
612             raise Exception("Internal method. Surely this class cannot be instanciated by itself.")
613         if pointer == 0:
614             return None
615         else:
616             o=object.__new__(cls)
617             o._as_parameter_=ctypes.c_void_p(pointer)
618             return o
619
620     @staticmethod
621     def from_param(arg):
622         '''(INTERNAL) ctypes parameter conversion method.
623         '''
624         return arg._as_parameter_
625     """ % {'name': classname})
626
627             if classname in overrides:
628                 self.output(overrides[classname])
629
630             prefix=self.prefixes.get(classname, '')
631
632             for cl, rtype, method, params, comment in el:
633                 if method in blacklist:
634                     continue
635                 # Strip prefix
636                 name=method.replace(prefix, '').replace('libvlc_', '')
637                 ret.add(method)
638                 if name in overriden_methods.get(cl, []):
639                     # Method already defined in override.py
640                     continue
641
642                 if params:
643                     params[0]=(params[0][0], 'self')
644                 if params and params[-1][0] in ('libvlc_exception_t*', 'mediacontrol_Exception*'):
645                     args=", ".join( p[1] for p in params[:-1] )
646                 else:
647                     args=", ".join( p[1] for p in params )
648
649                 self.output("    if hasattr(dll, '%s'):" % method)
650                 self.output("        def %s(%s):" % (name, args))
651                 self.output('            """%s\n        """' % self.fix_python_comment(comment))
652                 if params and params[-1][0] == 'libvlc_exception_t*':
653                     # Exception handling
654                     self.output("            e=VLCException()")
655                     self.output("            return %s(%s, e)" % (method, args))
656                 elif params and params[-1][0] == 'mediacontrol_Exception*':
657                     # Exception handling
658                     self.output("            e=MediaControlException()")
659                     self.output("            return %s(%s, e)" % (method, args))
660                 else:
661                     self.output("            return %s(%s)" % (method, args))
662                 self.output()
663
664                 # Check for standard methods
665                 if name == 'count':
666                     # There is a count method. Generate a __len__ one.
667                     if params and params[-1][0] == 'libvlc_exception_t*':
668                         self.output("""    def __len__(self):
669         e=VLCException()
670         return %s(self, e)
671 """ % method)
672                     else:
673                         # No exception
674                         self.output("""    def __len__(self):
675         return %s(self)
676 """ % method)
677                 elif name.endswith('item_at_index'):
678                     # Indexable (and thus iterable)"
679                     self.output("""    def __getitem__(self, i):
680         e=VLCException()
681         return %s(self, i, e)
682
683     def __iter__(self):
684         e=VLCException()
685         for i in xrange(len(self)):
686             yield self[i]
687 """ % method)
688         return ret
689
690 def process(output, list_of_includes):
691     p=Parser(list_of_includes)
692     g=PythonGenerator(p)
693     g.save(output)
694
695 if __name__ == '__main__':
696     opt=OptionParser(usage="""Parse VLC include files and generate bindings code.
697 %prog [options] include_file.h [...]""")
698
699     opt.add_option("-d", "--debug", dest="debug", action="store_true",
700                       default=False,
701                       help="Debug mode")
702
703     opt.add_option("-c", "--check", dest="check", action="store_true",
704                       default=False,
705                       help="Check mode")
706
707     opt.add_option("-o", "--output", dest="output", action="store",
708                       type="str", default="-",
709                       help="Output filename")
710
711     (options, args) = opt.parse_args()
712
713     if not args:
714         opt.print_help()
715         sys.exit(1)
716
717     p=Parser(args)
718     if options.check:
719         # Various consistency checks.
720         for (rt, name, params, comment) in p.methods:
721             if not comment.strip():
722                 print "No comment for %s" % name
723                 continue
724             names=comment_re.findall(comment)
725             if len(names) != len(params):
726                 print "Docstring comment parameters mismatch for %s" % name
727
728     if options.debug:
729         p.dump_methods()
730         p.dump_enums()
731
732     if options.check or options.debug:
733         sys.exit(0)
734
735     g=PythonGenerator(p)
736
737     g.save(options.output)