Add codegen to the tracked files
[platform/upstream/gst-editing-services.git] / bindings / python / codegen / codegen.py
1 import getopt
2 import keyword
3 import os
4 import string
5 import sys
6
7 import argtypes
8 import definitions
9 import defsparser
10 import override
11 import reversewrapper
12
13 class Coverage(object):
14     def __init__(self, name):
15         self.name = name
16         self.wrapped = 0
17         self.not_wrapped = 0
18
19     def declare_wrapped(self):
20         self.wrapped += 1
21
22     def declare_not_wrapped(self):
23         self.not_wrapped += 1
24
25     def printstats(self):
26         total = self.wrapped + self.not_wrapped
27         fd = sys.stderr
28         if total:
29             fd.write("***INFO*** The coverage of %s is %.2f%% (%i/%i)\n" %
30                      (self.name,
31                       float(self.wrapped*100)/total,
32                       self.wrapped,
33                       total))
34         else:
35             fd.write("***INFO*** There are no declared %s." % self.name)
36
37 functions_coverage = Coverage("global functions")
38 methods_coverage = Coverage("methods")
39 vproxies_coverage = Coverage("virtual proxies")
40 vaccessors_coverage = Coverage("virtual accessors")
41 iproxies_coverage = Coverage("interface proxies")
42
43 def exc_info():
44     #traceback.print_exc()
45     etype, value, tb = sys.exc_info()
46     ret = ""
47     try:
48         sval = str(value)
49         if etype == KeyError:
50             ret = "No ArgType for %s" % (sval,)
51         else:
52             ret = sval
53     finally:
54         del etype, value, tb
55     return ret
56
57 def fixname(name):
58     if keyword.iskeyword(name):
59         return name + '_'
60     return name
61
62 class FileOutput:
63     '''Simple wrapper for file object, that makes writing #line
64     statements easier.''' # "
65     def __init__(self, fp, filename=None):
66         self.fp = fp
67         self.lineno = 1
68         if filename:
69             self.filename = filename
70         else:
71             self.filename = self.fp.name
72     # handle writing to the file, and keep track of the line number ...
73     def write(self, str):
74         self.fp.write(str)
75         self.lineno = self.lineno + string.count(str, '\n')
76     def writelines(self, sequence):
77         for line in sequence:
78             self.write(line)
79     def close(self):
80         self.fp.close()
81     def flush(self):
82         self.fp.flush()
83
84     def setline(self, linenum, filename):
85         '''writes out a #line statement, for use by the C
86         preprocessor.''' # "
87         self.write('#line %d "%s"\n' % (linenum, filename))
88     def resetline(self):
89         '''resets line numbering to the original file'''
90         self.setline(self.lineno + 1, self.filename)
91
92 class Wrapper:
93     type_tmpl = (
94         'PyTypeObject Py%(typename)s_Type = {\n'
95         '    PyObject_HEAD_INIT(NULL)\n'
96         '    0,                                 /* ob_size */\n'
97         '    "%(classname)s",                   /* tp_name */\n'
98         '    sizeof(%(tp_basicsize)s),          /* tp_basicsize */\n'
99         '    0,                                 /* tp_itemsize */\n'
100         '    /* methods */\n'
101         '    (destructor)%(tp_dealloc)s,        /* tp_dealloc */\n'
102         '    (printfunc)0,                      /* tp_print */\n'
103         '    (getattrfunc)%(tp_getattr)s,       /* tp_getattr */\n'
104         '    (setattrfunc)%(tp_setattr)s,       /* tp_setattr */\n'
105         '    (cmpfunc)%(tp_compare)s,           /* tp_compare */\n'
106         '    (reprfunc)%(tp_repr)s,             /* tp_repr */\n'
107         '    (PyNumberMethods*)%(tp_as_number)s,     /* tp_as_number */\n'
108         '    (PySequenceMethods*)%(tp_as_sequence)s, /* tp_as_sequence */\n'
109         '    (PyMappingMethods*)%(tp_as_mapping)s,   /* tp_as_mapping */\n'
110         '    (hashfunc)%(tp_hash)s,             /* tp_hash */\n'
111         '    (ternaryfunc)%(tp_call)s,          /* tp_call */\n'
112         '    (reprfunc)%(tp_str)s,              /* tp_str */\n'
113         '    (getattrofunc)%(tp_getattro)s,     /* tp_getattro */\n'
114         '    (setattrofunc)%(tp_setattro)s,     /* tp_setattro */\n'
115         '    (PyBufferProcs*)%(tp_as_buffer)s,  /* tp_as_buffer */\n'
116         '    %(tp_flags)s,                      /* tp_flags */\n'
117         '    %(tp_doc)s,                        /* Documentation string */\n'
118         '    (traverseproc)%(tp_traverse)s,     /* tp_traverse */\n'
119         '    (inquiry)%(tp_clear)s,             /* tp_clear */\n'
120         '    (richcmpfunc)%(tp_richcompare)s,   /* tp_richcompare */\n'
121         '    %(tp_weaklistoffset)s,             /* tp_weaklistoffset */\n'
122         '    (getiterfunc)%(tp_iter)s,          /* tp_iter */\n'
123         '    (iternextfunc)%(tp_iternext)s,     /* tp_iternext */\n'
124         '    (struct PyMethodDef*)%(tp_methods)s, /* tp_methods */\n'
125         '    (struct PyMemberDef*)0,              /* tp_members */\n'
126         '    (struct PyGetSetDef*)%(tp_getset)s,  /* tp_getset */\n'
127         '    NULL,                              /* tp_base */\n'
128         '    NULL,                              /* tp_dict */\n'
129         '    (descrgetfunc)%(tp_descr_get)s,    /* tp_descr_get */\n'
130         '    (descrsetfunc)%(tp_descr_set)s,    /* tp_descr_set */\n'
131         '    %(tp_dictoffset)s,                 /* tp_dictoffset */\n'
132         '    (initproc)%(tp_init)s,             /* tp_init */\n'
133         '    (allocfunc)%(tp_alloc)s,           /* tp_alloc */\n'
134         '    (newfunc)%(tp_new)s,               /* tp_new */\n'
135         '    (freefunc)%(tp_free)s,             /* tp_free */\n'
136         '    (inquiry)%(tp_is_gc)s              /* tp_is_gc */\n'
137         '};\n\n'
138         )
139
140     slots_list = [
141         'tp_getattr', 'tp_setattr', 'tp_getattro', 'tp_setattro',
142         'tp_compare', 'tp_repr',
143         'tp_as_number', 'tp_as_sequence', 'tp_as_mapping', 'tp_hash',
144         'tp_call', 'tp_str', 'tp_as_buffer', 'tp_richcompare', 'tp_iter',
145         'tp_iternext', 'tp_descr_get', 'tp_descr_set', 'tp_init',
146         'tp_alloc', 'tp_new', 'tp_free', 'tp_is_gc',
147         'tp_traverse', 'tp_clear', 'tp_dealloc', 'tp_flags', 'tp_doc'
148         ]
149
150     getter_tmpl = (
151         'static PyObject *\n'
152         '%(funcname)s(PyObject *self, void *closure)\n'
153         '{\n'
154         '%(varlist)s'
155         '    ret = %(field)s;\n'
156         '%(codeafter)s\n'
157         '}\n\n'
158         )
159
160     parse_tmpl = (
161         '    if (!PyArg_ParseTupleAndKeywords(args, kwargs,'
162         '"%(typecodes)s:%(name)s"%(parselist)s))\n'
163         '        return %(errorreturn)s;\n'
164         )
165
166     deprecated_tmpl = (
167         '    if (PyErr_Warn(PyExc_DeprecationWarning, '
168         '"%(deprecationmsg)s") < 0)\n'
169         '        return %(errorreturn)s;\n'
170         )
171
172     methdef_tmpl = (
173         '    { "%(name)s", (PyCFunction)%(cname)s, %(flags)s,\n'
174         '      %(docstring)s },\n'
175         )
176
177     noconstructor = (
178         'static int\n'
179         'pygobject_no_constructor(PyObject *self, PyObject *args, '
180         'PyObject *kwargs)\n'
181         '{\n'
182         '    gchar buf[512];\n'
183         '\n'
184         '    g_snprintf(buf, sizeof(buf), "%s is an abstract widget", '
185         'self->ob_type->tp_name);\n'
186         '    PyErr_SetString(PyExc_NotImplementedError, buf);\n'
187         '    return -1;\n'
188         '}\n\n'
189         )
190
191     function_tmpl = (
192         'static PyObject *\n'
193         '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n'
194         '{\n'
195         '%(varlist)s'
196         '%(parseargs)s'
197         '%(codebefore)s'
198         '    %(begin_allow_threads)s\n'
199         '    %(setreturn)s%(cname)s(%(arglist)s);\n'
200         '    %(end_allow_threads)s\n'
201         '%(codeafter)s\n'
202         '}\n\n'
203         )
204
205     virtual_accessor_tmpl = (
206         'static PyObject *\n'
207         '_wrap_%(cname)s(PyObject *cls%(extraparams)s)\n'
208         '{\n'
209         '    gpointer klass;\n'
210         '%(varlist)s'
211         '%(parseargs)s'
212         '%(codebefore)s'
213         '    klass = g_type_class_ref(pyg_type_from_object(cls));\n'
214         '    if (%(class_cast_macro)s(klass)->%(virtual)s) {\n'
215         '        pyg_begin_allow_threads;\n'
216         '        %(setreturn)s%(class_cast_macro)s(klass)->'
217         '%(virtual)s(%(arglist)s);\n'
218         '        pyg_end_allow_threads;\n'
219         '    } else {\n'
220         '        PyErr_SetString(PyExc_NotImplementedError, '
221         '"virtual method %(name)s not implemented");\n'
222         '        g_type_class_unref(klass);\n'
223         '        return NULL;\n'
224         '    }\n'
225         '    g_type_class_unref(klass);\n'
226         '%(codeafter)s\n'
227         '}\n\n'
228         )
229
230     # template for method calls
231     constructor_tmpl = None
232     method_tmpl = None
233
234     def __init__(self, parser, objinfo, overrides, fp=FileOutput(sys.stdout)):
235         self.parser = parser
236         self.objinfo = objinfo
237         self.overrides = overrides
238         self.fp = fp
239
240     def get_lower_name(self):
241         return string.lower(string.replace(self.objinfo.typecode,
242                                            '_TYPE_', '_', 1))
243
244     def get_field_accessor(self, fieldname):
245         raise NotImplementedError
246
247     def get_initial_class_substdict(self): return {}
248
249     def get_initial_constructor_substdict(self, constructor):
250         return { 'name': '%s.__init__' % self.objinfo.c_name,
251                  'errorreturn': '-1' }
252     def get_initial_method_substdict(self, method):
253         substdict = { 'name': '%s.%s' % (self.objinfo.c_name, method.name) }
254         substdict['begin_allow_threads'] = 'pyg_begin_allow_threads;'
255         substdict['end_allow_threads'] = 'pyg_end_allow_threads;'
256         return substdict
257
258     def write_class(self):
259         if self.overrides.is_type_ignored(self.objinfo.c_name):
260             return
261         self.fp.write('\n/* ----------- %s ----------- */\n\n' %
262                       self.objinfo.c_name)
263         substdict = self.get_initial_class_substdict()
264         if not substdict.has_key('tp_flags'):
265             substdict['tp_flags'] = 'Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE'
266         substdict['typename'] = self.objinfo.c_name
267         if self.overrides.modulename:
268             substdict['classname'] = '%s.%s' % (self.overrides.modulename,
269                                            self.objinfo.name)
270         else:
271             substdict['classname'] = self.objinfo.name
272         substdict['tp_doc'] = self.objinfo.docstring
273
274         # Maybe this could be done in a nicer way, but I'll leave it as it is
275         # for now: -- Johan
276         if not self.overrides.slot_is_overriden('%s.tp_init' %
277                                                 self.objinfo.c_name):
278             substdict['tp_init'] = self.write_constructor()
279         substdict['tp_methods'] = self.write_methods()
280         substdict['tp_getset'] = self.write_getsets()
281
282         # handle slots ...
283         for slot in self.slots_list:
284
285             slotname = '%s.%s' % (self.objinfo.c_name, slot)
286             slotfunc = '_wrap_%s_%s' % (self.get_lower_name(), slot)
287             if slot[:6] == 'tp_as_':
288                 slotfunc = '&' + slotfunc
289             if self.overrides.slot_is_overriden(slotname):
290                 data = self.overrides.slot_override(slotname)
291                 self.write_function(slotname, data)
292                 substdict[slot] = slotfunc
293             else:
294                 if not substdict.has_key(slot):
295                     substdict[slot] = '0'
296
297         self.fp.write(self.type_tmpl % substdict)
298
299         self.write_virtuals()
300
301     def write_function_wrapper(self, function_obj, template,
302                                handle_return=0, is_method=0, kwargs_needed=0,
303                                substdict=None):
304         '''This function is the guts of all functions that generate
305         wrappers for functions, methods and constructors.'''
306         if not substdict: substdict = {}
307
308         info = argtypes.WrapperInfo()
309
310         substdict.setdefault('errorreturn', 'NULL')
311
312         # for methods, we want the leading comma
313         if is_method:
314             info.arglist.append('')
315
316         if function_obj.varargs:
317             raise ValueError, "varargs functions not supported"
318
319         for param in function_obj.params:
320             if param.pdflt and '|' not in info.parsestr:
321                 info.add_parselist('|', [], [])
322             handler = argtypes.matcher.get(param.ptype)
323             handler.write_param(param.ptype, param.pname, param.pdflt,
324                                 param.pnull, param.keeprefcount, info)
325
326         substdict['setreturn'] = ''
327         if handle_return:
328             if function_obj.ret not in ('none', None):
329                 substdict['setreturn'] = 'ret = '
330             handler = argtypes.matcher.get(function_obj.ret)
331             handler.write_return(function_obj.ret,
332                                  function_obj.caller_owns_return, info)
333
334         if function_obj.deprecated != None:
335             deprecated = self.deprecated_tmpl % {
336                 'deprecationmsg': function_obj.deprecated,
337                 'errorreturn': substdict['errorreturn'] }
338         else:
339             deprecated = ''
340
341         # if name isn't set, set it to function_obj.name
342         substdict.setdefault('name', function_obj.name)
343
344         substdict['begin_allow_threads'] = 'pyg_begin_allow_threads;'
345         substdict['end_allow_threads'] = 'pyg_end_allow_threads;'
346
347         if self.objinfo:
348             substdict['typename'] = self.objinfo.c_name
349         substdict.setdefault('cname',  function_obj.c_name)
350         substdict['varlist'] = info.get_varlist()
351         substdict['typecodes'] = info.parsestr
352         substdict['parselist'] = info.get_parselist()
353         substdict['arglist'] = info.get_arglist()
354         substdict['codebefore'] = deprecated + (
355             string.replace(info.get_codebefore(),
356             'return NULL', 'return ' + substdict['errorreturn'])
357             )
358         substdict['codeafter'] = (
359             string.replace(info.get_codeafter(),
360                            'return NULL',
361                            'return ' + substdict['errorreturn']))
362
363         if info.parsestr or kwargs_needed:
364             substdict['parseargs'] = self.parse_tmpl % substdict
365             substdict['extraparams'] = ', PyObject *args, PyObject *kwargs'
366             flags = 'METH_VARARGS|METH_KEYWORDS'
367
368             # prepend the keyword list to the variable list
369             substdict['varlist'] = info.get_kwlist() + substdict['varlist']
370         else:
371             substdict['parseargs'] = ''
372             substdict['extraparams'] = ''
373             flags = 'METH_NOARGS'
374
375         return template % substdict, flags
376
377     def write_constructor(self):
378         initfunc = '0'
379         constructor = self.parser.find_constructor(self.objinfo,self.overrides)
380         if not constructor:
381             return self.write_default_constructor()
382
383         funcname = constructor.c_name
384         try:
385             if self.overrides.is_overriden(funcname):
386                 data = self.overrides.override(funcname)
387                 self.write_function(funcname, data)
388                 self.objinfo.has_new_constructor_api = (
389                     self.objinfo.typecode in
390                     self.overrides.newstyle_constructors)
391             else:
392                 # ok, a hack to determine if we should use
393                 # new-style constructores :P
394                 property_based = getattr(self,
395                                          'write_property_based_constructor',
396                                          None)
397                 if property_based:
398                     if (len(constructor.params) == 0 or
399                         isinstance(constructor.params[0],
400                                    definitions.Property)):
401                         # write_property_based_constructor is only
402                         # implemented in GObjectWrapper
403                         return self.write_property_based_constructor(
404                             constructor)
405                     else:
406                         sys.stderr.write(
407                             "Warning: generating old-style constructor for:" +
408                             constructor.c_name + '\n')
409
410                 # write constructor from template ...
411                 code = self.write_function_wrapper(constructor,
412                     self.constructor_tmpl,
413                     handle_return=0, is_method=0, kwargs_needed=1,
414                     substdict=self.get_initial_constructor_substdict(
415                     constructor))[0]
416                 self.fp.write(code)
417             initfunc = '_wrap_' + funcname
418         except:
419             sys.stderr.write('Could not write constructor for %s: %s\n'
420                              % (self.objinfo.c_name, exc_info()))
421
422             initfunc = self.write_noconstructor()
423         return initfunc
424
425     def write_noconstructor(self):
426         # this is a hack ...
427         if not hasattr(self.overrides, 'no_constructor_written'):
428             self.fp.write(self.noconstructor)
429             self.overrides.no_constructor_written = 1
430         initfunc = 'pygobject_no_constructor'
431         return initfunc
432
433     def write_default_constructor(self):
434         return self.write_noconstructor()
435
436     def get_methflags(self, funcname):
437         if self.overrides.wants_kwargs(funcname):
438             flags = 'METH_VARARGS|METH_KEYWORDS'
439         elif self.overrides.wants_noargs(funcname):
440             flags = 'METH_NOARGS'
441         elif self.overrides.wants_onearg(funcname):
442             flags = 'METH_O'
443         else:
444             flags = 'METH_VARARGS'
445         if self.overrides.is_staticmethod(funcname):
446             flags += '|METH_STATIC'
447         elif self.overrides.is_classmethod(funcname):
448             flags += '|METH_CLASS'
449         return flags
450
451     def write_function(self, funcname, data):
452         lineno, filename = self.overrides.getstartline(funcname)
453         self.fp.setline(lineno, filename)
454         self.fp.write(data)
455         self.fp.resetline()
456         self.fp.write('\n\n')
457
458     def _get_class_virtual_substdict(self, meth, cname, parent):
459         substdict = self.get_initial_method_substdict(meth)
460         substdict['virtual'] = substdict['name'].split('.')[1]
461         substdict['cname'] = cname
462         substdict['class_cast_macro'] = parent.typecode.replace(
463             '_TYPE_', '_', 1) + "_CLASS"
464         substdict['typecode'] = self.objinfo.typecode
465         substdict['cast'] = string.replace(parent.typecode, '_TYPE_', '_', 1)
466         return substdict
467
468     def write_methods(self):
469         methods = []
470         klass = self.objinfo.c_name
471         # First, get methods from the defs files
472         for meth in self.parser.find_methods(self.objinfo):
473             method_name = meth.c_name
474             if self.overrides.is_ignored(method_name):
475                 continue
476             try:
477                 if self.overrides.is_overriden(method_name):
478                     if not self.overrides.is_already_included(method_name):
479                         data = self.overrides.override(method_name)
480                         self.write_function(method_name, data)
481
482                     methflags = self.get_methflags(method_name)
483                 else:
484                     # write constructor from template ...
485                     code, methflags = self.write_function_wrapper(meth,
486                         self.method_tmpl, handle_return=1, is_method=1,
487                         substdict=self.get_initial_method_substdict(meth))
488                     self.fp.write(code)
489                 methods.append(self.methdef_tmpl %
490                                { 'name':  fixname(meth.name),
491                                  'cname': '_wrap_' + method_name,
492                                  'flags': methflags,
493                                  'docstring': meth.docstring })
494                 methods_coverage.declare_wrapped()
495             except:
496                 methods_coverage.declare_not_wrapped()
497                 sys.stderr.write('Could not write method %s.%s: %s\n'
498                                 % (klass, meth.name, exc_info()))
499
500         # Now try to see if there are any defined in the override
501         for method_name in self.overrides.get_defines_for(klass):
502             c_name = override.class2cname(klass, method_name)
503             if self.overrides.is_already_included(method_name):
504                 continue
505
506             try:
507                 data = self.overrides.define(klass, method_name)
508                 self.write_function(method_name, data)
509                 methflags = self.get_methflags(method_name)
510
511                 methods.append(self.methdef_tmpl %
512                                { 'name':  method_name,
513                                  'cname': '_wrap_' + c_name,
514                                  'flags': methflags,
515                                  'docstring': meth.docstring })
516                 methods_coverage.declare_wrapped()
517             except:
518                 methods_coverage.declare_not_wrapped()
519                 sys.stderr.write('Could not write method %s.%s: %s\n'
520                                 % (klass, meth.name, exc_info()))
521
522         # Add GObject virtual method accessors, for chaining to parent
523         # virtuals from subclasses
524         methods += self.write_virtual_accessors()
525
526         if methods:
527             methoddefs = '_Py%s_methods' % self.objinfo.c_name
528             # write the PyMethodDef structure
529             methods.append('    { NULL, NULL, 0, NULL }\n')
530             self.fp.write('static const PyMethodDef %s[] = {\n' % methoddefs)
531             self.fp.write(string.join(methods, ''))
532             self.fp.write('};\n\n')
533         else:
534             methoddefs = 'NULL'
535         return methoddefs
536
537     def write_virtual_accessors(self):
538         klass = self.objinfo.c_name
539         methods = []
540         for meth in self.parser.find_virtuals(self.objinfo):
541             method_name = self.objinfo.c_name + "__do_" + meth.name
542             if self.overrides.is_ignored(method_name):
543                 continue
544             try:
545                 if self.overrides.is_overriden(method_name):
546                     if not self.overrides.is_already_included(method_name):
547                         data = self.overrides.override(method_name)
548                         self.write_function(method_name, data)
549                     methflags = self.get_methflags(method_name)
550                 else:
551                     # temporarily add a 'self' parameter as first argument
552                     meth.params.insert(0, definitions.Parameter(
553                         ptype=(self.objinfo.c_name + '*'),
554                         pname='self', pdflt=None, pnull=None))
555                     try:
556                         # write method from template ...
557                         code, methflags = self.write_function_wrapper(
558                             meth, self.virtual_accessor_tmpl,
559                             handle_return=True, is_method=False,
560                             substdict=self._get_class_virtual_substdict(
561                             meth, method_name, self.objinfo))
562                         self.fp.write(code)
563                     finally:
564                         del meth.params[0]
565                 methods.append(self.methdef_tmpl %
566                                { 'name':  "do_" + fixname(meth.name),
567                                  'cname': '_wrap_' + method_name,
568                                  'flags': methflags + '|METH_CLASS',
569                                  'docstring': 'NULL'})
570                 vaccessors_coverage.declare_wrapped()
571             except:
572                 vaccessors_coverage.declare_not_wrapped()
573                 sys.stderr.write(
574                     'Could not write virtual accessor method %s.%s: %s\n'
575                     % (klass, meth.name, exc_info()))
576         return methods
577
578     def write_virtuals(self):
579         '''
580         Write _wrap_FooBar__proxy_do_zbr() reverse wrapers for
581         GObject virtuals
582         '''
583         klass = self.objinfo.c_name
584         virtuals = []
585         for meth in self.parser.find_virtuals(self.objinfo):
586             method_name = self.objinfo.c_name + "__proxy_do_" + meth.name
587             if self.overrides.is_ignored(method_name):
588                 continue
589             try:
590                 if self.overrides.is_overriden(method_name):
591                     if not self.overrides.is_already_included(method_name):
592                         data = self.overrides.override(method_name)
593                         self.write_function(method_name, data)
594                 else:
595                     # write virtual proxy ...
596                     ret, props = argtypes.matcher.get_reverse_ret(meth.ret)
597                     wrapper = reversewrapper.ReverseWrapper(
598                         '_wrap_' + method_name, is_static=True)
599                     wrapper.set_return_type(ret(wrapper, **props))
600                     wrapper.add_parameter(reversewrapper.PyGObjectMethodParam(
601                         wrapper, "self", method_name="do_" + meth.name,
602                         c_type=(klass + ' *')))
603                     for param in meth.params:
604                         handler, props = argtypes.matcher.get_reverse(
605                             param.ptype)
606                         props["direction"] = param.pdir
607                         wrapper.add_parameter(handler(wrapper,
608                                                       param.pname, **props))
609                     buf = reversewrapper.MemoryCodeSink()
610                     wrapper.generate(buf)
611                     self.fp.write(buf.flush())
612                 virtuals.append((fixname(meth.name), '_wrap_' + method_name))
613                 vproxies_coverage.declare_wrapped()
614             except (KeyError, ValueError):
615                 vproxies_coverage.declare_not_wrapped()
616                 virtuals.append((fixname(meth.name), None))
617                 sys.stderr.write('Could not write virtual proxy %s.%s: %s\n'
618                                 % (klass, meth.name, exc_info()))
619         if virtuals:
620             # Write a 'pygtk class init' function for this object,
621             # except when the object type is explicitly ignored (like
622             # GtkPlug and GtkSocket on win32).
623             if self.overrides.is_ignored(self.objinfo.typecode):
624                 return
625             class_cast_macro = self.objinfo.typecode.replace(
626                 '_TYPE_', '_', 1) + "_CLASS"
627             cast_macro = self.objinfo.typecode.replace('_TYPE_', '_', 1)
628             funcname = "__%s_class_init" % klass
629             self.objinfo.class_init_func = funcname
630             have_implemented_virtuals = not not [True
631                                                  for name, cname in virtuals
632                                                      if cname is not None]
633             self.fp.write(
634             ('\nstatic int\n'
635              '%(funcname)s(gpointer gclass, PyTypeObject *pyclass)\n'
636              '{\n') % vars())
637
638             if have_implemented_virtuals:
639                 self.fp.write('    PyObject *o;\n')
640                 self.fp.write(
641                     '    %(klass)sClass *klass = '
642                     '%(class_cast_macro)s(gclass);\n'
643                     '    PyObject *gsignals = '
644                     'PyDict_GetItemString(pyclass->tp_dict, "__gsignals__");\n'
645                     % vars())
646
647             for name, cname in virtuals:
648                 do_name = 'do_' + name
649                 if cname is None:
650                     self.fp.write('\n    /* overriding %(do_name)s '
651                                   'is currently not supported */\n' % vars())
652                 else:
653                     self.fp.write('''
654     o = PyObject_GetAttrString((PyObject *) pyclass, "%(do_name)s");
655     if (o == NULL)
656         PyErr_Clear();
657     else {
658         if (!PyObject_TypeCheck(o, &PyCFunction_Type)
659             && !(gsignals && PyDict_GetItemString(gsignals, "%(name)s")))
660             klass->%(name)s = %(cname)s;
661         Py_DECREF(o);
662     }
663 ''' % vars())
664             self.fp.write('    return 0;\n}\n')
665
666     def write_getsets(self):
667         lower_name = self.get_lower_name()
668         getsets_name = lower_name + '_getsets'
669         getterprefix = '_wrap_' + lower_name + '__get_'
670         setterprefix = '_wrap_' + lower_name + '__set_'
671
672         # no overrides for the whole function.  If no fields,
673         # don't write a func
674         if not self.objinfo.fields:
675             return '0'
676         getsets = []
677         for ftype, cfname in self.objinfo.fields:
678             fname = cfname.replace('.', '_')
679             gettername = '0'
680             settername = '0'
681             attrname = self.objinfo.c_name + '.' + fname
682             if self.overrides.attr_is_overriden(attrname):
683                 code = self.overrides.attr_override(attrname)
684                 self.write_function(attrname, code)
685                 if string.find(code, getterprefix + fname) >= 0:
686                     gettername = getterprefix + fname
687                 if string.find(code, setterprefix + fname) >= 0:
688                     settername = setterprefix + fname
689             if gettername == '0':
690                 try:
691                     funcname = getterprefix + fname
692                     info = argtypes.WrapperInfo()
693                     handler = argtypes.matcher.get(ftype)
694                     # for attributes, we don't own the "return value"
695                     handler.write_return(ftype, 0, info)
696                     self.fp.write(self.getter_tmpl %
697                                   { 'funcname': funcname,
698                                     'varlist': info.varlist,
699                                     'field': self.get_field_accessor(cfname),
700                                     'codeafter': info.get_codeafter() })
701                     gettername = funcname
702                 except:
703                     sys.stderr.write(
704                         "Could not write getter for %s.%s: %s\n"
705                         % (self.objinfo.c_name, fname, exc_info()))
706             if gettername != '0' or settername != '0':
707                 getsets.append('    { "%s", (getter)%s, (setter)%s },\n' %
708                                (fixname(fname), gettername, settername))
709
710         if not getsets:
711             return '0'
712         self.fp.write('static const PyGetSetDef %s[] = {\n' % getsets_name)
713         for getset in getsets:
714             self.fp.write(getset)
715         self.fp.write('    { NULL, (getter)0, (setter)0 },\n')
716         self.fp.write('};\n\n')
717
718         return getsets_name
719
720     def write_functions(self, prefix):
721         self.fp.write('\n/* ----------- functions ----------- */\n\n')
722         functions = []
723
724         # First, get methods from the defs files
725         for func in self.parser.find_functions():
726             funcname = func.c_name
727             if self.overrides.is_ignored(funcname):
728                 continue
729             try:
730                 if self.overrides.is_overriden(funcname):
731                     data = self.overrides.override(funcname)
732                     self.write_function(funcname, data)
733
734                     methflags = self.get_methflags(funcname)
735                 else:
736                     # write constructor from template ...
737                     code, methflags = self.write_function_wrapper(func,
738                         self.function_tmpl, handle_return=1, is_method=0)
739                     self.fp.write(code)
740                 functions.append(self.methdef_tmpl %
741                                  { 'name':  func.name,
742                                    'cname': '_wrap_' + funcname,
743                                    'flags': methflags,
744                                    'docstring': func.docstring })
745                 functions_coverage.declare_wrapped()
746             except:
747                 functions_coverage.declare_not_wrapped()
748                 sys.stderr.write('Could not write function %s: %s\n'
749                                  % (func.name, exc_info()))
750
751         # Now try to see if there are any defined in the override
752         for funcname in self.overrides.get_functions():
753             try:
754                 data = self.overrides.function(funcname)
755                 self.write_function(funcname, data)
756                 methflags = self.get_methflags(funcname)
757                 functions.append(self.methdef_tmpl %
758                                  { 'name':  funcname,
759                                    'cname': '_wrap_' + funcname,
760                                    'flags': methflags,
761                                    'docstring': 'NULL'})
762                 functions_coverage.declare_wrapped()
763             except:
764                 functions_coverage.declare_not_wrapped()
765                 sys.stderr.write('Could not write function %s: %s\n'
766                                  % (funcname, exc_info()))
767
768         # write the PyMethodDef structure
769         functions.append('    { NULL, NULL, 0, NULL }\n')
770
771         self.fp.write('const PyMethodDef ' + prefix + '_functions[] = {\n')
772         self.fp.write(string.join(functions, ''))
773         self.fp.write('};\n\n')
774
775 class GObjectWrapper(Wrapper):
776     constructor_tmpl = (
777         'static int\n'
778         '_wrap_%(cname)s(PyGObject *self%(extraparams)s)\n'
779         '{\n'
780         '%(varlist)s'
781         '%(parseargs)s'
782         '%(codebefore)s'
783         '    self->obj = (GObject *)%(cname)s(%(arglist)s);\n'
784         '%(codeafter)s\n'
785         '    if (!self->obj) {\n'
786         '        PyErr_SetString(PyExc_RuntimeError, '
787         '"could not create %(typename)s object");\n'
788         '        return -1;\n'
789         '    }\n'
790         '%(aftercreate)s'
791         '    pygobject_register_wrapper((PyObject *)self);\n'
792         '    return 0;\n'
793         '}\n\n'
794         )
795
796     method_tmpl = (
797         'static PyObject *\n'
798         '_wrap_%(cname)s(PyGObject *self%(extraparams)s)\n'
799         '{\n'
800         '%(varlist)s'
801         '%(parseargs)s'
802         '%(codebefore)s'
803         '    %(begin_allow_threads)s\n'
804         '    %(setreturn)s%(cname)s(%(cast)s(self->obj)%(arglist)s);\n'
805         '    %(end_allow_threads)s\n'
806         '%(codeafter)s\n'
807         '}\n\n'
808         )
809     def __init__(self, parser, objinfo, overrides, fp=FileOutput(sys.stdout)):
810         Wrapper.__init__(self, parser, objinfo, overrides, fp)
811         if self.objinfo:
812             self.castmacro = string.replace(self.objinfo.typecode,
813                                             '_TYPE_', '_', 1)
814
815     def get_initial_class_substdict(self):
816         return { 'tp_basicsize'      : 'PyGObject',
817                  'tp_weaklistoffset' : 'offsetof(PyGObject, weakreflist)',
818                  'tp_dictoffset'     : 'offsetof(PyGObject, inst_dict)' }
819
820     def get_field_accessor(self, fieldname):
821         castmacro = string.replace(self.objinfo.typecode, '_TYPE_', '_', 1)
822         return '%s(pygobject_get(self))->%s' % (castmacro, fieldname)
823
824     def get_initial_constructor_substdict(self, constructor):
825         substdict = Wrapper.get_initial_constructor_substdict(self,
826                                                               constructor)
827         if not constructor.caller_owns_return:
828             substdict['aftercreate'] = "    g_object_ref(self->obj);\n"
829         else:
830             substdict['aftercreate'] = ''
831         return substdict
832
833     def get_initial_method_substdict(self, method):
834         substdict = Wrapper.get_initial_method_substdict(self, method)
835         substdict['cast'] = string.replace(self.objinfo.typecode,
836                                            '_TYPE_', '_', 1)
837         return substdict
838
839     def write_default_constructor(self):
840         try:
841             parent = self.parser.find_object(self.objinfo.parent)
842         except ValueError:
843             parent = None
844         if parent is not None:
845             ## just like the constructor is inheritted, we should
846             # inherit the new API compatibility flag
847             self.objinfo.has_new_constructor_api = (
848                 parent.has_new_constructor_api)
849         elif self.objinfo.parent == 'GObject':
850             self.objinfo.has_new_constructor_api = True
851         return '0'
852
853     def write_property_based_constructor(self, constructor):
854         self.objinfo.has_new_constructor_api = True
855         out = self.fp
856         print >> out, "static int"
857         print >> out, '_wrap_%s(PyGObject *self, PyObject *args,' \
858               ' PyObject *kwargs)\n{' % constructor.c_name
859         if constructor.params:
860             s = "    GType obj_type = pyg_type_from_object((PyObject *) self);"
861             print >> out, s
862
863         def py_str_list_to_c(arg):
864             if arg:
865                 return "{" + ", ".join(
866                     map(lambda s: '"' + s + '"', arg)) + ", NULL }"
867             else:
868                 return "{ NULL }"
869
870         classname = '%s.%s' % (self.overrides.modulename,
871                                self.objinfo.name)
872
873         if constructor.params:
874             mandatory_arguments = [param for param in constructor.params
875                                              if not param.optional]
876             optional_arguments = [param for param in constructor.params
877                                             if param.optional]
878             arg_names = py_str_list_to_c(
879             [param.argname
880              for param in mandatory_arguments + optional_arguments])
881
882             prop_names = py_str_list_to_c(
883             [param.pname
884              for param in mandatory_arguments + optional_arguments])
885
886             print >> out, "    GParameter params[%i];" % \
887                   len(constructor.params)
888             print >> out, "    PyObject *parsed_args[%i] = {NULL, };" % \
889                   len(constructor.params)
890             print >> out, "    char *arg_names[] = %s;" % arg_names
891             print >> out, "    char *prop_names[] = %s;" % prop_names
892             print >> out, "    guint nparams, i;"
893             print >> out
894             if constructor.deprecated is not None:
895                 out.write(
896                     '    if (PyErr_Warn(PyExc_DeprecationWarning, '
897                     '"%s") < 0)\n' %
898                     constructor.deprecated)
899                 print >> out, '        return -1;'
900                 print >> out
901             out.write("    if (!PyArg_ParseTupleAndKeywords(args, kwargs, ")
902             template = '"'
903             if mandatory_arguments:
904                 template += "O"*len(mandatory_arguments)
905             if optional_arguments:
906                 template += "|" + "O"*len(optional_arguments)
907             template += ':%s.__init__"' % classname
908             print >> out, template, ", arg_names",
909             for i in range(len(constructor.params)):
910                 print >> out, ", &parsed_args[%i]" % i,
911
912             out.write(
913                 "))\n"
914                 "        return -1;\n"
915                 "\n"
916                 "    memset(params, 0, sizeof(GParameter)*%i);\n"
917                 "    if (!pyg_parse_constructor_args(obj_type, arg_names,\n"
918                 "                                    prop_names, params, \n"
919                 "                                    &nparams, parsed_args))\n"
920                 "        return -1;\n"
921                 "    pygobject_constructv(self, nparams, params);\n"
922                 "    for (i = 0; i < nparams; ++i)\n"
923                 "        g_value_unset(&params[i].value);\n"
924                 % len(constructor.params))
925         else:
926             out.write(
927                 "    static char* kwlist[] = { NULL };\n"
928                 "\n")
929
930             if constructor.deprecated is not None:
931                 out.write(
932                     '    if (PyErr_Warn(PyExc_DeprecationWarning, "%s") < 0)\n'
933                     '        return -1;\n'
934                     '\n' % constructor.deprecated)
935
936             out.write(
937                 '    if (!PyArg_ParseTupleAndKeywords(args, kwargs,\n'
938                 '                                     ":%s.__init__",\n'
939                 '                                     kwlist))\n'
940                 '        return -1;\n'
941                 '\n'
942                 '    pygobject_constructv(self, 0, NULL);\n' % classname)
943         out.write(
944             '    if (!self->obj) {\n'
945             '        PyErr_SetString(\n'
946             '            PyExc_RuntimeError, \n'
947             '            "could not create %s object");\n'
948             '        return -1;\n'
949             '    }\n' % classname)
950
951         if not constructor.caller_owns_return:
952             print >> out, "    g_object_ref(self->obj);\n"
953
954         out.write(
955             '    return 0;\n'
956             '}\n\n')
957
958         return "_wrap_%s" % constructor.c_name
959
960
961 ## TODO : Add GstMiniObjectWrapper(Wrapper)
962 class GstMiniObjectWrapper(Wrapper):
963     constructor_tmpl = (
964         'static int\n'
965         '_wrap_%(cname)s(PyGstMiniObject *self%(extraparams)s)\n'
966         '{\n'
967         '%(varlist)s'
968         '%(parseargs)s'
969         '%(codebefore)s'
970         '    self->obj = (GstMiniObject *)%(cname)s(%(arglist)s);\n'
971         '%(codeafter)s\n'
972         '    if (!self->obj) {\n'
973         '        PyErr_SetString(PyExc_RuntimeError, '
974         '"could not create %(typename)s miniobject");\n'
975         '        return -1;\n'
976         '    }\n'
977         '%(aftercreate)s'
978         '    pygstminiobject_register_wrapper((PyObject *)self);\n'
979         '    return 0;\n'
980         '}\n\n'
981         )
982     method_tmpl = (
983         'static PyObject *\n'
984         '_wrap_%(cname)s(PyGstMiniObject *self%(extraparams)s)\n'
985         '{\n'
986         '%(varlist)s'
987         '%(parseargs)s'
988         '%(codebefore)s'
989         '    %(begin_allow_threads)s\n'
990         '    %(setreturn)s%(cname)s(%(cast)s(self->obj)%(arglist)s);\n'
991         '    %(end_allow_threads)s\n'
992         '%(codeafter)s\n'
993         '}\n\n'
994         )
995     def __init__(self, parser, objinfo, overrides, fp=FileOutput(sys.stdout)):
996         Wrapper.__init__(self, parser, objinfo, overrides, fp)
997         if self.objinfo:
998             self.castmacro = string.replace(self.objinfo.typecode,
999                                             '_TYPE_', '_', 1)
1000
1001     def get_initial_class_substdict(self):
1002         return { 'tp_basicsize'      : 'PyGstMiniObject',
1003                  'tp_weaklistoffset' : 'offsetof(PyGstMiniObject, weakreflist)',
1004                  'tp_dictoffset'     : 'offsetof(PyGstMiniObject, inst_dict)' }
1005     
1006     def get_field_accessor(self, fieldname):
1007         castmacro = string.replace(self.objinfo.typecode, '_TYPE_', '_', 1)
1008         return '%s(pygstminiobject_get(self))->%s' % (castmacro, fieldname)
1009
1010     def get_initial_constructor_substdict(self, constructor):
1011         substdict = Wrapper.get_initial_constructor_substdict(self,
1012                                                               constructor)
1013         if not constructor.caller_owns_return:
1014             substdict['aftercreate'] = "    gst_mini_object_ref(self->obj);\n"
1015         else:
1016             substdict['aftercreate'] = ''
1017         return substdict
1018
1019     def get_initial_method_substdict(self, method):
1020         substdict = Wrapper.get_initial_method_substdict(self, method)
1021         substdict['cast'] = string.replace(self.objinfo.typecode,
1022                                            '_TYPE_', '_', 1)
1023         return substdict
1024     
1025
1026
1027 class GInterfaceWrapper(GObjectWrapper):
1028     virtual_accessor_tmpl = (
1029         'static PyObject *\n'
1030         '_wrap_%(cname)s(PyObject *cls%(extraparams)s)\n'
1031         '{\n'
1032         '    %(vtable)s *iface;\n'
1033         '%(varlist)s'
1034         '%(parseargs)s'
1035         '%(codebefore)s'
1036         '    iface = g_type_interface_peek('
1037         'g_type_class_peek(pyg_type_from_object(cls)), %(typecode)s);\n'
1038         '    if (iface->%(virtual)s)\n'
1039         '        %(setreturn)siface->%(virtual)s(%(arglist)s);\n'
1040         '    else {\n'
1041         '        PyErr_SetString(PyExc_NotImplementedError, '
1042         '"interface method %(name)s not implemented");\n'
1043         '        return NULL;\n'
1044         '    }\n'
1045         '%(codeafter)s\n'
1046         '}\n\n'
1047         )
1048
1049     def get_initial_class_substdict(self):
1050         return { 'tp_basicsize'      : 'PyObject',
1051                  'tp_weaklistoffset' : '0',
1052                  'tp_dictoffset'     : '0'}
1053
1054     def write_constructor(self):
1055         # interfaces have no constructors ...
1056         return '0'
1057     def write_getsets(self):
1058         # interfaces have no fields ...
1059         return '0'
1060
1061     def _get_class_virtual_substdict(self, meth, cname, parent):
1062         substdict = self.get_initial_method_substdict(meth)
1063         substdict['virtual'] = substdict['name'].split('.')[1]
1064         substdict['cname'] = cname
1065         substdict['typecode'] = self.objinfo.typecode
1066         substdict['vtable'] = self.objinfo.vtable
1067         return substdict
1068
1069     def write_virtuals(self):
1070         ## Now write reverse method wrappers, which let python code
1071         ## implement interface methods.
1072         # First, get methods from the defs files
1073         klass = self.objinfo.c_name
1074         proxies = []
1075         for meth in self.parser.find_virtuals(self.objinfo):
1076             method_name = self.objinfo.c_name + "__proxy_do_" + meth.name
1077             if self.overrides.is_ignored(method_name):
1078                 continue
1079             try:
1080                 if self.overrides.is_overriden(method_name):
1081                     if not self.overrides.is_already_included(method_name):
1082                         data = self.overrides.override(method_name)
1083                         self.write_function(method_name, data)
1084                 else:
1085                     # write proxy ...
1086                     ret, props = argtypes.matcher.get_reverse_ret(meth.ret)
1087                     wrapper = reversewrapper.ReverseWrapper(
1088                         '_wrap_' + method_name, is_static=True)
1089                     wrapper.set_return_type(ret(wrapper, **props))
1090                     wrapper.add_parameter(reversewrapper.PyGObjectMethodParam(
1091                         wrapper, "self", method_name="do_" + meth.name,
1092                         c_type=(klass + ' *')))
1093                     for param in meth.params:
1094                         handler, props = argtypes.matcher.get_reverse(
1095                             param.ptype)
1096                         props["direction"] = param.pdir
1097                         wrapper.add_parameter(
1098                             handler(wrapper, param.pname, **props))
1099                     buf = reversewrapper.MemoryCodeSink()
1100                     wrapper.generate(buf)
1101                     self.fp.write(buf.flush())
1102                 proxies.append((fixname(meth.name), '_wrap_' + method_name))
1103                 iproxies_coverage.declare_wrapped()
1104             except (KeyError, ValueError):
1105                 iproxies_coverage.declare_not_wrapped()
1106                 proxies.append((fixname(meth.name), None))
1107                 sys.stderr.write('Could not write interface proxy %s.%s: %s\n'
1108                                 % (klass, meth.name, exc_info()))
1109
1110         if not proxies:
1111             return
1112
1113         # Make sure we have at least one proxy function
1114         if not [cname for name,cname in proxies if not cname is None]:
1115             return
1116         
1117         ## Write an interface init function for this object
1118         funcname = "__%s__interface_init" % klass
1119         vtable = self.objinfo.vtable
1120         self.fp.write(
1121             '\nstatic void\n'
1122             '%(funcname)s(%(vtable)s *iface, PyTypeObject *pytype)\n'
1123             '{\n'
1124             '    %(vtable)s *parent_iface = '
1125             'g_type_interface_peek_parent(iface);\n'
1126             '    PyObject *py_method;\n'
1127             '\n'
1128             % vars())
1129
1130         for name, cname in proxies:
1131             do_name = 'do_' + name
1132             if cname is None:
1133                 continue
1134
1135             self.fp.write((
1136                 '    py_method = pytype? PyObject_GetAttrString('
1137                 '(PyObject *) pytype, "%(do_name)s") : NULL;\n'
1138                 '    if (py_method && !PyObject_TypeCheck(py_method, '
1139                 '&PyCFunction_Type)) {\n'
1140                 '        iface->%(name)s = %(cname)s;\n'
1141                 '    } else {\n'
1142                 '        PyErr_Clear();\n'
1143                 '        if (parent_iface) {\n'
1144                 '            iface->%(name)s = parent_iface->%(name)s;\n'
1145                 '        }\n'
1146                 '    Py_XDECREF(py_method);\n'
1147                 '    }\n'
1148                 ) % vars())
1149         self.fp.write('}\n\n')
1150         interface_info = "__%s__iinfo" % klass
1151         self.fp.write('''
1152 static const GInterfaceInfo %s = {
1153     (GInterfaceInitFunc) %s,
1154     NULL,
1155     NULL
1156 };
1157 ''' % (interface_info, funcname))
1158         self.objinfo.interface_info = interface_info
1159
1160 class GBoxedWrapper(Wrapper):
1161     constructor_tmpl = (
1162         'static int\n'
1163         '_wrap_%(cname)s(PyGBoxed *self%(extraparams)s)\n'
1164         '{\n' \
1165         '%(varlist)s'
1166         '%(parseargs)s'
1167         '%(codebefore)s'
1168         '    self->gtype = %(typecode)s;\n'
1169         '    self->free_on_dealloc = FALSE;\n'
1170         '    self->boxed = %(cname)s(%(arglist)s);\n'
1171         '%(codeafter)s\n'
1172         '    if (!self->boxed) {\n'
1173         '        PyErr_SetString(PyExc_RuntimeError, '
1174         '"could not create %(typename)s object");\n'
1175         '        return -1;\n'
1176         '    }\n'
1177         '    self->free_on_dealloc = TRUE;\n'
1178         '    return 0;\n'
1179         '}\n\n'
1180         )
1181
1182     method_tmpl = (
1183         'static PyObject *\n'
1184         '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n'
1185         '{\n'
1186         '%(varlist)s'
1187         '%(parseargs)s'
1188         '%(codebefore)s'
1189         '    %(begin_allow_threads)s\n'
1190         '    %(setreturn)s%(cname)s(pyg_boxed_get(self, '
1191         '%(typename)s)%(arglist)s);\n'
1192         '    %(end_allow_threads)s\n'
1193         '%(codeafter)s\n'
1194         '}\n\n'
1195         )
1196
1197     def get_initial_class_substdict(self):
1198         return { 'tp_basicsize'      : 'PyGBoxed',
1199                  'tp_weaklistoffset' : '0',
1200                  'tp_dictoffset'     : '0' }
1201
1202     def get_field_accessor(self, fieldname):
1203         return 'pyg_boxed_get(self, %s)->%s' % (self.objinfo.c_name, fieldname)
1204
1205     def get_initial_constructor_substdict(self, constructor):
1206         substdict = Wrapper.get_initial_constructor_substdict(
1207             self, constructor)
1208         substdict['typecode'] = self.objinfo.typecode
1209         return substdict
1210
1211 class GPointerWrapper(GBoxedWrapper):
1212     constructor_tmpl = (
1213         'static int\n'
1214         '_wrap_%(cname)s(PyGPointer *self%(extraparams)s)\n'
1215         '{\n'
1216         '%(varlist)s'
1217         '%(parseargs)s'
1218         '%(codebefore)s'
1219         '    self->gtype = %(typecode)s;\n'
1220         '    self->pointer = %(cname)s(%(arglist)s);\n'
1221         '%(codeafter)s\n'
1222         '    if (!self->pointer) {\n'
1223         '        PyErr_SetString(PyExc_RuntimeError, '
1224         '"could not create %(typename)s object");\n'
1225         '        return -1;\n'
1226         '    }\n'
1227         '    return 0;\n'
1228         '}\n\n'
1229         )
1230
1231     method_tmpl = (
1232         'static PyObject *\n'
1233         '_wrap_%(cname)s(PyObject *self%(extraparams)s)\n'
1234         '{\n'
1235         '%(varlist)s'
1236         '%(parseargs)s'
1237         '%(codebefore)s'
1238         '    %(setreturn)s%(cname)s(pyg_pointer_get(self, '
1239         '%(typename)s)%(arglist)s);\n'
1240         '%(codeafter)s\n'
1241         '}\n\n'
1242         )
1243
1244     def get_initial_class_substdict(self):
1245         return { 'tp_basicsize'      : 'PyGPointer',
1246                  'tp_weaklistoffset' : '0',
1247                  'tp_dictoffset'     : '0' }
1248
1249     def get_field_accessor(self, fieldname):
1250         return 'pyg_pointer_get(self, %s)->%s' % (self.objinfo.c_name,
1251                                                   fieldname)
1252
1253     def get_initial_constructor_substdict(self, constructor):
1254         substdict = Wrapper.get_initial_constructor_substdict(
1255             self, constructor)
1256         substdict['typecode'] = self.objinfo.typecode
1257         return substdict
1258
1259 def write_headers(data, fp):
1260     fp.write('/* -- THIS FILE IS GENERATED - DO NOT EDIT */')
1261     fp.write('/* -*- Mode: C; c-basic-offset: 4 -*- */\n\n')
1262     fp.write('#include <Python.h>\n\n\n')
1263     fp.write(data)
1264     fp.resetline()
1265     fp.write('\n\n')
1266
1267 def write_body(data, fp):
1268     fp.write(data)
1269     fp.resetline()
1270     fp.write('\n\n')
1271
1272 def write_imports(overrides, fp):
1273     fp.write('/* ---------- types from other modules ---------- */\n')
1274     for module, pyname, cname in overrides.get_imports():
1275         fp.write('static PyTypeObject *_%s;\n' % cname)
1276         fp.write('#define %s (*_%s)\n' % (cname, cname))
1277     fp.write('\n\n')
1278
1279 def write_type_declarations(parser, fp):
1280     fp.write('/* ---------- forward type declarations ---------- */\n')
1281     for obj in parser.boxes:
1282         fp.write('PyTypeObject Py' + obj.c_name + '_Type;\n')
1283     for obj in parser.objects:
1284         fp.write('PyTypeObject Py' + obj.c_name + '_Type;\n')
1285     for obj in parser.miniobjects:
1286         fp.write('PyTypeObject Py' + obj.c_name + '_Type;\n')
1287     for interface in parser.interfaces:
1288         fp.write('PyTypeObject Py' + interface.c_name + '_Type;\n')
1289     fp.write('\n')
1290
1291
1292 def sort_parent_children(objects):
1293     objects = list(objects)
1294     modified = True
1295     while modified:
1296         modified = False
1297         parent_index = None
1298         child_index = None
1299         for i, obj in enumerate(objects):
1300             if obj.parent == 'GObject':
1301                 continue
1302             if obj.parent not in [info.c_name for info in objects[:i]]:
1303                 for j, info in enumerate(objects[i+1:]):
1304                     if info.c_name == obj.parent:
1305                         parent_index = i + 1 + j
1306                         child_index = i
1307                         break
1308                 else:
1309                     continue
1310                 break
1311         if child_index is not None and parent_index is not None:
1312             if child_index != parent_index:
1313                 objects.insert(child_index, objects.pop(parent_index))
1314                 modified = True
1315     return objects
1316
1317 def write_classes(parser, overrides, fp):
1318     ## Sort the objects, so that we generate code for the parent types
1319     ## before their children.
1320     objects = sort_parent_children(parser.objects)
1321     for klass, items in ((GBoxedWrapper, parser.boxes),
1322                          (GPointerWrapper, parser.pointers),
1323                          (GObjectWrapper, objects),
1324                          (GstMiniObjectWrapper, parser.miniobjects),
1325                          (GInterfaceWrapper, parser.interfaces)):
1326         for item in items:
1327             instance = klass(parser, item, overrides, fp)
1328             instance.write_class()
1329             fp.write('\n')
1330
1331 def write_enums(parser, overrides, prefix, fp=sys.stdout):
1332     if not parser.enums:
1333         return
1334     fp.write('\n/* ----------- enums and flags ----------- */\n\n')
1335     fp.write(
1336         'void\n' + prefix +
1337         '_add_constants(PyObject *module, const gchar *strip_prefix)\n{\n')
1338
1339     for enum in parser.enums:
1340         if overrides.is_type_ignored(enum.c_name):
1341             continue
1342         if enum.typecode is None:
1343             for nick, value in enum.values:
1344                 fp.write(
1345                     '    PyModule_AddIntConstant(module, '
1346                     '(char *) pyg_constant_strip_prefix("%s", strip_prefix), %s);\n'
1347                     % (value, value))
1348         else:
1349             if enum.deftype == 'enum':
1350                 fp.write('  pyg_enum_add(module, "%s", strip_prefix, %s);\n'
1351                          % (enum.name, enum.typecode))
1352             else:
1353                 fp.write('  pyg_flags_add(module, "%s", strip_prefix, %s);\n'
1354                          % (enum.name, enum.typecode))
1355
1356     fp.write('\n')
1357     fp.write('  if (PyErr_Occurred())\n')
1358     fp.write('    PyErr_Print();\n')
1359     fp.write('}\n\n')
1360
1361 def write_extension_init(overrides, prefix, fp):
1362     fp.write('/* initialise stuff extension classes */\n')
1363     fp.write('void\n' + prefix + '_register_classes(PyObject *d)\n{\n')
1364     imports = overrides.get_imports()[:]
1365     if imports:
1366         bymod = {}
1367         for module, pyname, cname in imports:
1368             bymod.setdefault(module, []).append((pyname, cname))
1369         fp.write('    PyObject *module;\n\n')
1370         for module in bymod:
1371             fp.write(
1372                 '    if ((module = PyImport_ImportModule("%s")) != NULL) {\n'
1373                 % module)
1374             fp.write(
1375                 '        PyObject *moddict = PyModule_GetDict(module);\n\n')
1376             for pyname, cname in bymod[module]:
1377                 fp.write(
1378                     '        _%s = (PyTypeObject *)PyDict_GetItemString('
1379                     'moddict, "%s");\n' % (cname, pyname))
1380                 fp.write('        if (_%s == NULL) {\n' % cname)
1381                 fp.write('            PyErr_SetString(PyExc_ImportError,\n')
1382                 fp.write('                "cannot import name %s from %s");\n'
1383                          % (pyname, module))
1384                 fp.write('            return;\n')
1385                 fp.write('        }\n')
1386             fp.write('    } else {\n')
1387             fp.write('        PyErr_SetString(PyExc_ImportError,\n')
1388             fp.write('            "could not import %s");\n' % module)
1389             fp.write('        return;\n')
1390             fp.write('    }\n')
1391         fp.write('\n')
1392     fp.write(overrides.get_init() + '\n')
1393     fp.resetline()
1394
1395 def write_registers(parser, overrides, fp):
1396     for boxed in parser.boxes:
1397         if overrides.is_type_ignored(boxed.c_name):
1398             continue
1399         fp.write('    pyg_register_boxed(d, "' + boxed.name +
1400                  '", ' + boxed.typecode +
1401                  ', &Py' + boxed.c_name +
1402                  '_Type);\n')
1403     for pointer in parser.pointers:
1404         if overrides.is_type_ignored(pointer.c_name):
1405             continue
1406         fp.write('    pyg_register_pointer(d, "' + pointer.name +
1407                  '", ' + pointer.typecode +
1408                  ', &Py' + pointer.c_name + '_Type);\n')
1409     for interface in parser.interfaces:
1410         if overrides.is_type_ignored(interface.c_name):
1411             continue
1412         fp.write('    pyg_register_interface(d, "' + interface.name +
1413                  '", '+ interface.typecode + ', &Py' + interface.c_name +
1414                  '_Type);\n')
1415         if interface.interface_info is not None:
1416             fp.write('    pyg_register_interface_info(%s, &%s);\n' %
1417                      (interface.typecode, interface.interface_info))
1418
1419     objects = parser.objects[:]
1420     pos = 0
1421     while pos < len(objects):
1422         parent = objects[pos].parent
1423         for i in range(pos+1, len(objects)):
1424             if objects[i].c_name == parent:
1425                 objects.insert(i+1, objects[pos])
1426                 del objects[pos]
1427                 break
1428         else:
1429             pos = pos + 1
1430     for obj in objects:
1431         if overrides.is_type_ignored(obj.c_name):
1432             continue
1433         bases = []
1434         if obj.parent != None:
1435             bases.append(obj.parent)
1436         bases = bases + obj.implements
1437         if bases:
1438             fp.write('    pygobject_register_class(d, "' + obj.c_name +
1439                      '", ' + obj.typecode + ', &Py' + obj.c_name +
1440                      '_Type, Py_BuildValue("(' + 'O' * len(bases) + ')", ' +
1441                      string.join(map(lambda s: '&Py'+s+'_Type', bases), ', ') +
1442                      '));\n')
1443         else:
1444             fp.write('    pygobject_register_class(d, "' + obj.c_name +
1445                      '", ' + obj.typecode + ', &Py' + obj.c_name +
1446                      '_Type, NULL);\n')
1447         if obj.has_new_constructor_api:
1448             fp.write('    pyg_set_object_has_new_constructor(%s);\n' %
1449                      obj.typecode)
1450         else:
1451             print >> sys.stderr, (
1452                 "Warning: Constructor for %s needs to be updated to new API\n"
1453                 "         See http://live.gnome.org/PyGTK_2fWhatsNew28"
1454                 "#update-constructors") % obj.c_name
1455         if obj.class_init_func is not None:
1456             fp.write('    pyg_register_class_init(%s, %s);\n' %
1457                      (obj.typecode, obj.class_init_func))
1458     #TODO: register mini-objects
1459     miniobjects = parser.miniobjects[:]
1460     for obj in miniobjects:
1461         bases = []
1462         if obj.parent != None:
1463             bases.append(obj.parent)
1464         bases = bases + obj.implements
1465         if bases:
1466             fp.write('    pygstminiobject_register_class(d, "' + obj.c_name +
1467                      '", ' + obj.typecode + ', &Py' + obj.c_name +
1468                      '_Type, Py_BuildValue("(' + 'O' * len(bases) + ')", ' +
1469                      string.join(map(lambda s: '&Py'+s+'_Type', bases), ', ') +
1470                      '));\n')
1471         else:
1472             fp.write('    pygstminiobject_register_class(d, "' + obj.c_name +
1473                      '", ' + obj.typecode + ', &Py' + obj.c_name +
1474                      '_Type, NULL);\n')
1475        
1476     fp.write('}\n')
1477
1478 def write_source(parser, overrides, prefix, fp=FileOutput(sys.stdout)):
1479     write_headers(overrides.get_headers(), fp)
1480     write_imports(overrides, fp)
1481     write_type_declarations(parser, fp)
1482     write_body(overrides.get_body(), fp)
1483     write_classes(parser, overrides, fp)
1484
1485     wrapper = Wrapper(parser, None, overrides, fp)
1486     wrapper.write_functions(prefix)
1487
1488     write_enums(parser, overrides, prefix, fp)
1489     write_extension_init(overrides, prefix, fp)
1490     write_registers(parser, overrides, fp)
1491
1492 def register_types(parser):
1493     for boxed in parser.boxes:
1494         argtypes.matcher.register_boxed(boxed.c_name, boxed.typecode)
1495     for pointer in parser.pointers:
1496         argtypes.matcher.register_pointer(pointer.c_name, pointer.typecode)
1497     for obj in parser.objects:
1498         argtypes.matcher.register_object(obj.c_name, obj.parent, obj.typecode)
1499     for obj in parser.miniobjects:
1500         argtypes.matcher.register_miniobject(obj.c_name, obj.parent, obj.typecode)
1501     for obj in parser.interfaces:
1502         argtypes.matcher.register_object(obj.c_name, None, obj.typecode)
1503     for enum in parser.enums:
1504         if enum.deftype == 'flags':
1505             argtypes.matcher.register_flag(enum.c_name, enum.typecode)
1506         else:
1507             argtypes.matcher.register_enum(enum.c_name, enum.typecode)
1508
1509 usage = 'usage: codegen.py [-o overridesfile] [-p prefix] defsfile'
1510 def main(argv):
1511     o = override.Overrides()
1512     prefix = 'pygtk'
1513     outfilename = None
1514     errorfilename = None
1515     extendpath = []
1516     opts, args = getopt.getopt(argv[1:], "o:p:r:t:D:x",
1517                         ["override=", "prefix=", "register=", "outfilename=",
1518                          "load-types=", "errorfilename=", "extendpath="])
1519     defines = {} # -Dkey[=val] options
1520
1521     for opt, arg in opts:
1522         if opt in ('-x', '--extendpath'):
1523             extendpath.append(arg)
1524     extendpath.insert(0, os.getcwd())
1525     o = override.Overrides(path=extendpath)
1526     
1527     for opt, arg in opts:
1528         if opt in ('-o', '--override'):
1529             o = override.Overrides(arg, path=extendpath)
1530         elif opt in ('-p', '--prefix'):
1531             prefix = arg
1532         elif opt in ('-r', '--register'):
1533             # Warning: user has to make sure all -D options appear before -r
1534             p = defsparser.DefsParser(arg, defines)
1535             p.startParsing()
1536             register_types(p)
1537             del p
1538         elif opt == '--outfilename':
1539             outfilename = arg
1540         elif opt == '--errorfilename':
1541             errorfilename = arg
1542         elif opt in ('-t', '--load-types'):
1543             globals = {}
1544             execfile(arg, globals)
1545         elif opt == '-D':
1546             nameval = arg.split('=')
1547             try:
1548                 defines[nameval[0]] = nameval[1]
1549             except IndexError:
1550                 defines[nameval[0]] = None
1551     if len(args) < 1:
1552         print >> sys.stderr, usage
1553         return 1
1554     if errorfilename:
1555         sys.stderr = open(errorfilename, "w")
1556     p = defsparser.DefsParser(args[0], defines)
1557     if not outfilename:
1558         outfilename = os.path.splitext(args[0])[0] + '.c'
1559
1560     p.startParsing()
1561
1562     register_types(p)
1563     write_source(p, o, prefix, FileOutput(sys.stdout, outfilename))
1564
1565     functions_coverage.printstats()
1566     methods_coverage.printstats()
1567     vproxies_coverage.printstats()
1568     vaccessors_coverage.printstats()
1569     iproxies_coverage.printstats()
1570
1571 if __name__ == '__main__':
1572     sys.exit(main(sys.argv))