source: trunk/python2/class_parser.py @ 196

Last change on this file since 196 was 196, checked in by tim, 14 years ago

experimental python bindings generator as provided by Michael Cohen

File size: 98.9 KB
Line 
1import sys, os, re, pdb, StringIO
2 
3DEBUG = 0
4
5def log(msg):
6    if DEBUG>0:
7        sys.stderr.write(msg+"\n")
8
9def escape_for_string(string):
10    result = string
11    result = result.encode("string-escape")
12    result = result.replace('"',r'\"')
13
14    return result
15
16class Module:
17    def __init__(self, name):
18        self.name = name
19        self.constants = set()
20        self.classes = {}
21        self.headers = '#include <Python.h>\n'
22        self.files = []
23
24    def __str__(self):
25        result = "Module %s\n" % (self.name)
26        l = self.classes.values()
27        l.sort()
28        for attr in l:
29            if attr.is_active():
30                result += "    %s\n" % attr
31
32        l = list(self.constants)
33        l.sort()
34        result += 'Constants:\n'
35        for attr, type in l:
36            result += " %s\n" % attr
37
38        return result
39
40    init_string = ''
41    def initialization(self):
42        result = self.init_string + """
43talloc_set_log_fn((void *)printf);
44//talloc_enable_leak_report();
45"""
46        for cls in self.classes.values():
47            if cls.is_active():
48                result += cls.initialise()
49
50        return result
51
52    def add_constant(self, constant, type="numeric"):
53        """ This will be called to add #define constant macros """
54        self.constants.add((constant, type))
55
56    def add_class(self, cls, handler):
57        self.classes[cls.class_name] = cls
58
59        ## Make a wrapper in the type dispatcher so we can handle
60        ## passing this class from/to python
61        type_dispatcher[cls.class_name] = handler
62
63    def private_functions(self):
64        """ Emits hard coded private functions for doing various things """
65        return """
66/* The following is a static array mapping CLASS() pointers to their
67python wrappers. This is used to allow the correct wrapper to be
68chosen depending on the object type found - regardless of the
69prototype.
70
71This is basically a safer way for us to cast the correct python type
72depending on context rather than assuming a type based on the .h
73definition. For example consider the function
74
75AFFObject Resolver.open(uri, mode)
76
77The .h file implies that an AFFObject object is returned, but this is
78not true as most of the time an object of a derived class will be
79returned. In C we cast the returned value to the correct type. In the
80python wrapper we just instantiate the correct python object wrapper
81at runtime depending on the actual returned type. We use this lookup
82table to do so.
83*/
84static int TOTAL_CLASSES=0;
85
86/* This is a global reference to this module so classes can call each
87   other.
88*/
89static PyObject *g_module = NULL;
90
91static struct python_wrapper_map_t {
92       Object class_ref;
93       PyTypeObject *python_type;
94} python_wrappers[%s];
95
96/** This is a generic wrapper type */
97typedef struct {
98  PyObject_HEAD
99  void *base;
100  void *ctx;
101} Gen_wrapper;
102
103/* Create the relevant wrapper from the item based on the lookup
104table.
105*/
106Gen_wrapper *new_class_wrapper(Object item) {
107   int i;
108   Gen_wrapper *result;
109   Object cls;
110
111   /* Hack to get the current null_context */
112   void* null_context;
113   int* tmp = talloc(NULL, int);
114   null_context = talloc_parent(tmp);
115   talloc_free(tmp);
116
117   // Return None for a NULL pointer
118   if(!item) {
119     Py_INCREF(Py_None);
120     return (Gen_wrapper *)Py_None;
121   };
122
123   // Search for subclasses
124   for(cls=(Object)item->__class__; cls != cls->__super__; cls=cls->__super__) {
125     for(i=0; i<TOTAL_CLASSES; i++) {
126       if(python_wrappers[i].class_ref == cls) {
127         PyErr_Clear();
128
129         result = (Gen_wrapper *)_PyObject_New(python_wrappers[i].python_type);
130         result->ctx = talloc_asprintf(NULL, "new_class_wrapper %%s@%%p", NAMEOF(item), item);
131         result->base = (void *)item;
132
133         /* If its owned by the null_context it means that the function does
134            not want to own the memory - we therefore steal it so it gets freed
135            with the python object. */
136         if(talloc_parent(result->base) == null_context)
137                  talloc_steal(result->ctx, result->base);
138
139         return result;
140       };
141     };
142   };
143
144  PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %%s", NAMEOF(item));
145  return NULL;
146};
147
148static PyObject *resolve_exception(char **error_buff) {
149  enum _error_type *type = aff4_get_current_error(error_buff);
150  switch(*type) {
151case EProgrammingError:
152    return PyExc_SystemError;
153case EKeyError:
154    return PyExc_KeyError;
155case ERuntimeError:
156    return PyExc_RuntimeError;
157case EWarning:
158    return PyExc_AssertionError;
159default:
160    return PyExc_RuntimeError;
161};
162};
163
164static int type_check(PyObject *obj, PyTypeObject *type) {
165   PyTypeObject *tmp;
166
167   // Recurse through the inheritance tree and check if the types are expected
168   if(obj)
169     for(tmp = obj->ob_type; tmp && tmp != &PyBaseObject_Type; tmp = tmp->tp_base) {
170       if(tmp == type) return 1;
171     };
172
173  return 0;
174};
175
176static int check_error() {
177   if(!CheckError(EZero)) {
178         char *buffer;
179         PyObject *exception = resolve_exception(&buffer);
180
181         PyErr_Format(exception, "%%s", buffer);
182         ClearError();
183         return 1;
184   };
185  return 0;
186};
187
188#define CHECK_ERROR if(check_error()) goto error;
189
190""" % (len(self.classes)+1)
191
192    def initialise_class(self, class_name, out, done = None):
193        if done and class_name in done: return
194
195        done.add(class_name)
196
197        cls = self.classes[class_name]
198        """ Write out class initialisation code into the main init function. """
199        if cls.is_active():
200            base_class = self.classes.get(cls.base_class_name)
201
202            if base_class and base_class.is_active():
203                ## We have a base class - ensure it gets written out
204                ## first:
205                self.initialise_class(cls.base_class_name, out, done)
206
207                ## Now assign ourselves as derived from them
208                out.write(" %s_Type.tp_base = &%s_Type;" % (
209                        cls.class_name, cls.base_class_name))
210           
211            out.write("""
212 %(name)s_Type.tp_new = PyType_GenericNew;
213 if (PyType_Ready(&%(name)s_Type) < 0)
214     return;
215
216 Py_INCREF((PyObject *)&%(name)s_Type);
217 PyModule_AddObject(m, "%(name)s", (PyObject *)&%(name)s_Type);
218""" % {'name': cls.class_name})
219
220    def write(self, out):
221        ## Prepare all classes
222        for cls in self.classes.values():
223            cls.prepare()
224
225        out.write("""
226/**********************************************************************
227     Autogenerated module %s
228
229This module was autogenerated from the following files:
230""" % self.name)
231        for file in self.files:
232            out.write("%s\n" % file)
233
234        out.write("\nThis module implements the following classes:\n")
235        out.write(self.__str__())
236        out.write("""***********************************************************************/
237""")
238        out.write(self.headers)
239        out.write(self.private_functions())
240
241        for cls in self.classes.values():
242            if cls.is_active():
243                out.write("/******************** %s ***********************/" % cls.class_name)
244                cls.struct(out)
245                cls.prototypes(out)
246
247        out.write("/*****************************************************\n             Implementation\n******************************************************/\n\n")
248        for cls in self.classes.values():
249            if cls.is_active():
250                cls.PyMethodDef(out)
251                cls.code(out)
252                cls.PyTypeObject(out)
253
254        ## Write the module initializer
255        out.write("""
256static PyMethodDef %(module)s_methods[] = {
257     {NULL}  /* Sentinel */
258};
259
260PyMODINIT_FUNC init%(module)s(void) {
261   /* Make sure threads are enabled */
262   PyEval_InitThreads();
263
264   /* create module */
265   PyObject *m = Py_InitModule3("%(module)s", %(module)s_methods,
266                                   "%(module)s module.");
267   PyObject *d = PyModule_GetDict(m);
268   PyObject *tmp;
269
270   g_module = m;
271""" % {'module': self.name})
272
273        ## The trick is to initialise the classes in order of their
274        ## inheritance. The following code will order initializations
275        ## according to their inheritance tree
276        done = set()
277        for class_name in self.classes.keys():
278            self.initialise_class(class_name, out, done)
279
280        ## Add the constants in here
281        for constant, type in self.constants:
282            if type == 'integer':
283                out.write(""" tmp = PyLong_FromUnsignedLongLong((int64_t)%s); \n""" % constant)
284            elif type == 'string':
285                out.write(" tmp = PyString_FromString((char *)%s); \n" % constant)
286            else:
287                out.write(" // I dont know how to convert %s type %s\n" % (constant, type))
288                continue
289
290            out.write("""
291 PyDict_SetItemString(d, "%s", tmp);
292 Py_DECREF(tmp);\n""" % (constant))
293
294        out.write(self.initialization())
295        out.write("}\n\n")
296
297class Type:
298    interface = None
299    buildstr = 'O'
300    sense = 'IN'
301    error_value = "return 0;"
302    active = True
303
304    def __init__(self, name, type):
305        self.name = name
306        self.type = type
307        self.attributes = set()
308
309    def comment(self):
310        return "%s %s " % (self.type, self.name)
311
312    def python_name(self):
313        return self.name
314
315    def python_proxy_post_call(self):
316        """ This is called after a proxy call """
317        return ''
318
319    def returned_python_definition(self, *arg, **kw):
320        return self.definition(*arg, **kw)
321
322    def definition(self, default=None, **kw):
323        if default:
324            return "%s %s=%s;\n" % (self.type, self.name, default)
325        else:
326            return "%s __attribute__((unused)) %s;\n" % (self.type, self.name)
327
328    def byref(self):
329        return "&%s" % self.name
330
331    def call_arg(self):
332        return self.name
333
334    def pre_call(self, method):
335        return ''
336
337    def assign(self, call, method, target=None):
338        return "Py_BEGIN_ALLOW_THREADS\n%s = %s;\nPy_END_ALLOW_THREADS\n" % (target or self.name, call)
339
340    def post_call(self, method):
341        if "DESTRUCTOR" in self.attributes:
342            return "talloc_free(self->ctx); self->base = NULL;\n"
343
344        return ''
345
346    def from_python_object(self, source, destination, method, **kw):
347        return ''
348
349    def return_value(self, value):
350        return "return %s;" % value
351
352    def __str__(self):
353        if self.name == 'func_return':
354            return self.type
355        if 'void' in self.type:
356            return ''
357
358        result = "%s : %s" % (self.type, self.name)
359
360        return result
361
362class String(Type):
363    interface = 'string'
364    buildstr = 's'
365    error_value = "return NULL;"
366
367    def __init__(self, name, type):
368        Type.__init__(self, name, type)
369        self.length = "strlen(%s)" % name
370
371    def byref(self):
372        return "&%s" % self.name
373
374    def to_python_object(self, name=None, result='py_result',**kw):
375        name = name or self.name
376
377        result = """PyErr_Clear();
378   if(!%(name)s) { PyErr_Format(PyExc_RuntimeError, "%(name)s is NULL"); goto error; };
379   %(result)s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\nif(!%(result)s) goto error;
380""" % dict(name=name, result=result,length=self.length)
381
382        if "BORROWED" not in self.attributes and 'BORROWED' not in kw:
383            result += "talloc_unlink(NULL, %s);\n" % name
384
385        return result
386
387    def from_python_object(self, source, destination, method, context='NULL'):
388        method.error_set = True
389        return """
390{
391  char *buff; Py_ssize_t length;
392
393  PyErr_Clear();
394  if(-1==PyString_AsStringAndSize(%(source)s, &buff, &length))
395     goto error;
396
397  %(destination)s = talloc_size(%(context)s, length + 1);
398  memcpy(%(destination)s, buff, length);
399  %(destination)s[length]=0;
400};
401""" % dict(source = source, destination = destination, context =context)
402
403class ZString(String):
404    interface = 'null_terminated_string'
405
406class BorrowedString(String):
407    def to_python_object(self, name=None, result='py_result', **kw):
408        name = name or self.name
409        return "PyErr_Clear();\n" +\
410            "%s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\n" % dict(
411            name=name, length=self.length, result=result)
412
413class Char_and_Length(Type):
414    interface = 'char_and_length'
415    buildstr = 's#'
416    error_value = "return NULL;"
417
418    def __init__(self, data, data_type, length, length_type):
419        Type.__init__(self, data, data_type)
420
421        self.name = data
422        self.data_type=data_type
423        self.length = length
424        self.length_type = length_type
425
426    def comment(self):
427        return "%s %s, %s %s" % (self.data_type, self.name,
428                                 self.length_type, self.length)
429
430    def definition(self, default = '""', **kw):
431        return "char *%s=%s; Py_ssize_t %s=strlen(%s);\n" % (
432            self.name, default,
433            self.length, default)
434
435    def byref(self):
436        return "&%s, &%s" % (self.name, self.length)
437
438    def call_arg(self):
439        return "(%s)%s, (%s)%s" % (self.data_type, self.name, self.length_type,
440                                   self.length)
441
442    def to_python_object(self, name=None, result='py_result', **kw):
443        return "PyErr_Clear();\n"\
444            "%s = PyString_FromStringAndSize(%s, %s);\nif(!%s) goto error;" % (
445            result, self.name, self.length, result);
446
447class Integer(Type):
448    interface = 'integer'
449    buildstr = 'K'
450
451    def __init__(self, name,type):
452        Type.__init__(self,name,type)
453        self.type = 'uint64_t'
454        self.original_type = type
455
456    def definition(self, default = 0, **kw):
457        return Type.definition(self, default)
458
459    def to_python_object(self, name=None, result='py_result', **kw):
460        name = name or self.name
461        return "PyErr_Clear();\n%s = PyLong_FromLongLong(%s);\n" % (result, name)
462
463    def from_python_object(self, source, destination, method, **kw):
464        return "PyErr_Clear();\n"\
465            "%(destination)s = PyLong_AsUnsignedLong(%(source)s);\n" % dict(
466            source = source, destination= destination)
467
468    def comment(self):
469        return "%s %s " % (self.original_type, self.name)
470
471class Integer32(Integer):
472    buildstr = 'I'
473
474    def __init__(self, name,type):
475        Type.__init__(self,name,type)
476        self.type = 'unsigned int '
477        self.original_type = type
478
479    def to_python_object(self, name=None, result='py_result', **kw):
480        name = name or self.name
481        return "PyErr_Clear();\n%s = PyLong_FromLong(%s);\n" % (result, name)
482
483class Integer64(Integer):
484    buildstr = 'K'
485    type = 'unsigned int'
486
487class Char(Integer):
488    buildstr = "s"
489    interface = 'small_integer'
490
491    def to_python_object(self, name = None, result = 'py_result', **kw):
492        ## We really want to return a string here
493        return """{ char *str_%(name)s = &%(name)s;
494    PyErr_Clear();
495    %(result)s = PyString_FromStringAndSize(str_%(name)s, 1);
496if(!%(result)s) goto error;
497};
498""" % dict(result=result, name = name or self.name)
499
500    def definition(self, default = '"\\x0"', **kw):
501        ## Shut up unused warnings
502        return "char %s __attribute__((unused))=0;\nchar *str_%s __attribute__((unused)) = %s;\n" % (
503            self.name,self.name, default)
504
505    def byref(self):
506        return "&str_%s" % self.name
507
508    def pre_call(self, method):
509        method.error_set = True
510        return """
511if(strlen(str_%(name)s)!=1) {
512  PyErr_Format(PyExc_RuntimeError,
513          "You must only provide a single character for arg %(name)r");
514  goto error;
515};
516
517%(name)s = str_%(name)s[0];
518""" % dict(name = self.name)
519
520class StringOut(String):
521    sense = 'OUT'
522
523class IntegerOut(Integer):
524    sense = 'OUT_DONE'
525    buildstr = ''
526
527    def python_name(self):
528        return None
529
530    def byref(self):
531        return ''
532
533    def call_arg(self):
534        return "&%s" % self.name
535
536class Integer32Out(Integer32):
537    sense = 'OUT_DONE'
538    buildstr = ''
539
540    def python_name(self):
541        return None
542
543    def byref(self):
544        return ''
545
546    def call_arg(self):
547        return "&%s" % self.name
548
549class Char_and_Length_OUT(Char_and_Length):
550    sense = 'OUT_DONE'
551    buildstr = 'l'
552
553    def definition(self, default = 0, **kw):
554        return "char *%s=NULL; Py_ssize_t %s=%s;\n" % (
555            self.name,
556            self.length, default) + "PyObject *tmp_%s;\n" % self.name
557
558    def python_name(self):
559        return self.length
560
561    def byref(self):
562        return "&%s" % self.length
563
564    def pre_call(self, method):
565        return """PyErr_Clear();
566tmp_%s = PyString_FromStringAndSize(NULL, %s);
567if(!tmp_%s) goto error;
568PyString_AsStringAndSize(tmp_%s, &%s, (Py_ssize_t *)&%s);
569""" % (self.name, self.length, self.name, self.name, self.name, self.length)
570
571    def to_python_object(self, name=None, result='py_result', sense='in', **kw):
572        name = name or self.name
573        if 'results' in kw:
574            kw['results'].pop(0)
575
576        if sense=='proxied':
577            return "py_%s = PyLong_FromLong(%s);\n" % (self.name, self.length)
578
579        return  """if(func_return > %(length)s) {
580  func_return = 0;
581};
582
583_PyString_Resize(&tmp_%(name)s, func_return); \n%(result)s = tmp_%(name)s;\n""" % \
584            dict(name= name, result= result, length=self.length)
585
586
587    def python_proxy_post_call(self, result='py_result'):
588        return """
589{
590    char *tmp_buff; int tmp_len;
591    if(-1==PyString_AsStringAndSize(%(result)s, &tmp_buff, &tmp_len)) goto error;
592
593    memcpy(%(name)s,tmp_buff, tmp_len);
594    Py_DECREF(%(result)s);
595    %(result)s = PyLong_FromLong(tmp_len);
596};
597""" % dict(result = result, name=self.name)
598
599class TDB_DATA_P(Char_and_Length_OUT):
600    bare_type = "TDB_DATA"
601
602    def __init__(self, name, type):
603        Type.__init__(self, name, type)
604
605    def definition(self, default=None, **kw):
606        return Type.definition(self)
607
608    def byref(self):
609        return "%s.dptr, &%s.dsize" % (self.name, self.name)
610
611    def pre_call(self, method):
612        return ''
613
614    def call_arg(self):
615        return Type.call_arg(self)
616
617    def to_python_object(self, name=None,result='py_result', **kw):
618        name = name or self.name
619        return "PyErr_Clear();"\
620            "%s = PyString_FromStringAndSize((char *)%s->dptr, %s->dsize);"\
621            "\ntalloc_free(%s);" % (result,
622                                    name, name, name)
623
624    def from_python_object(self, source, destination, method, **kw):
625        method.error_set = True
626        return """
627%(destination)s = talloc(self, %(bare_type)s);
628{ Py_ssize_t tmp; char *buf;
629
630  PyErr_Clear();
631  if(-1==PyString_AsStringAndSize(%(source)s, &buf, &tmp)) {
632  goto error;
633};
634
635  // Take a copy of the python string
636  %(destination)s->dptr = talloc_memdup(%(destination)s, buf, tmp);
637  %(destination)s->dsize = tmp;
638}
639// We no longer need the python object
640Py_DECREF(%(source)s);
641""" % dict(source = source, destination = destination, 
642           bare_type = self.bare_type)
643
644class TDB_DATA(TDB_DATA_P):
645    def to_python_object(self, name = None, result='py_result', **kw):
646        name = name or self.name
647
648        return "PyErr_Clear();\n"\
649            "%s = PyString_FromStringAndSize((char *)%s.dptr, %s.dsize);\n" % (
650            result,
651            name, name)
652
653class Void(Type):
654    buildstr = ''
655    error_value = "return;"
656    original_type = ''
657
658    def __init__(self, *args):
659        Type.__init__(self, None, 'void *')
660
661    def definition(self, default = None, **kw):
662        return ''
663
664    def to_python_object(self, name=None, result = 'py_result', **kw):
665        return "Py_INCREF(Py_None); py_result = Py_None;\n"
666
667    def call_arg(self):
668        return "NULL"
669
670    def byref(self):
671        return None
672
673    def assign(self, call, method, target=None):
674        ## We dont assign the result to anything
675        return "Py_BEGIN_ALLOW_THREADS\n%s;\nPy_END_ALLOW_THREADS\n" % call
676
677    def return_value(self, value):
678        return "return;"
679
680class StringArray(String):
681    interface = 'array'
682    buildstr = 'O'
683
684    def definition(self, default = '""', **kw):
685        return "char **%s=NULL; PyObject *py_%s=NULL;\n" % (
686            self.name, self.name)
687
688    def byref(self):
689        return "&py_%s" % (self.name)
690
691    def from_python_object(self, source, destination, method, context='NULL'):
692        method.error_set = True
693        return """{
694Py_ssize_t i,size=0;
695
696if(%(source)s) {
697   if(!PySequence_Check(%(source)s)) {
698     PyErr_Format(PyExc_ValueError, "%(destination)s must be a sequence");
699     goto error;
700   };
701
702   size = PySequence_Size(%(source)s);
703};
704
705%(destination)s = talloc_zero_array(NULL, char *, size + 1);
706
707for(i=0; i<size;i++) {
708 PyObject *tmp = PySequence_GetItem(%(source)s, i);
709 if(!tmp) goto error;
710 %(destination)s[i] = PyString_AsString(tmp);
711 if(!%(destination)s[i]) {
712   Py_DECREF(tmp);
713   goto error;
714 };
715 Py_DECREF(tmp);
716};
717
718};""" % dict(source = source, destination = destination, context = context)
719
720    def pre_call(self, method):
721        return self.from_python_object("py_%s" % self.name, self.name, method)
722
723    def error_condition(self):
724        return """if(%s) talloc_free(%s);\n""" % (self.name, self.name)
725
726class Wrapper(Type):
727    """ This class represents a wrapped C type """
728    sense = 'IN'
729    error_value = "return NULL;"
730
731    def from_python_object(self, source, destination, method, **kw):
732        return """
733/* First check that the returned value is in fact a Wrapper */
734if(!type_check(%(source)s, &%(type)s_Type)) {
735  PyErr_Format(PyExc_RuntimeError, "function must return an %(type)s instance");
736  goto error;
737};
738
739%(destination)s = ((Gen_wrapper *)%(source)s)->base;
740""" % dict(source = source, destination = destination, type = self.type)
741
742    def to_python_object(self, **kw):
743        return ''
744
745    def returned_python_definition(self, default = 'NULL', sense='in', **kw):
746        return "%s %s;\n" % (self.type, self.name)
747
748    def definition(self, default = 'NULL', sense='in', **kw):
749        result = "Gen_wrapper *%s __attribute__((unused)) = %s;\n" % (self.name, default)
750        if sense == 'in' and not 'OUT' in self.attributes:
751            result += " %s __attribute__((unused)) call_%s;\n" % (self.type, self.name)
752
753        return result
754
755    def call_arg(self):
756        return "call_%s" % self.name
757
758    def pre_call(self, method):
759        if 'OUT' in self.attributes or self.sense == 'OUT':
760            return ''
761
762        return """
763if(!%(name)s || (PyObject *)%(name)s==Py_None) {
764   call_%(name)s = NULL;
765} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
766     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
767     goto error;
768} else {
769   call_%(name)s = %(name)s->base;
770};\n""" % self.__dict__
771
772    def assign(self, call, method, target=None):
773        method.error_set = True;
774        args = dict(name = target or self.name, call = call, type = self.type)
775
776        result = """{
777       Object returned_object;
778
779       ClearError();
780
781       Py_BEGIN_ALLOW_THREADS
782       returned_object = (Object)%(call)s;
783       Py_END_ALLOW_THREADS
784
785       CHECK_ERROR;
786""" % args
787
788        ## Is NULL an acceptable return type? In some python code NULL
789        ## can be returned (e.g. in iterators) but usually it should
790        ## be converted to None.
791        if "NULL_OK" in self.attributes:
792            result += """if(returned_object == NULL) {
793     %(name)s = NULL; """ % args
794        else:
795            result += """
796       // A NULL return without errors means we return None
797       if(!returned_object) {
798         %(name)s = (Gen_wrapper *)Py_None;
799         Py_INCREF(Py_None);
800""" % args
801
802        result += """
803       } else {
804         //printf("%%s: Wrapping %%s@%%p\\n", __FUNCTION__, NAMEOF(returned_object), returned_object);
805         %(name)s = new_class_wrapper(returned_object);
806         if(!%(name)s) goto error;
807""" % args
808
809        if "BORROWED" in self.attributes:
810            result += "           talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
811
812        result += """       };
813    }
814"""
815        return result
816
817    def to_python_object(self, name=None, result = 'py_result', sense='in', **kw):
818        name = name or self.name
819        args = dict(result=result,
820                    name = name)
821
822        if sense=='proxied':
823            return "%(result)s = (PyObject *)new_class_wrapper((Object)%(name)s);\n" % args
824
825        return "%(result)s = (PyObject *)%(name)s;\n" % args
826
827class PointerWrapper(Wrapper):
828    """ A pointer to a wrapped class """
829    def __init__(self, name, type):
830        type = type.split()[0]
831        Wrapper.__init__(self,name, type)
832
833    def definition(self, default = 'NULL', sense='in', **kw):
834        result = "Gen_wrapper *%s = %s;" % (self.name, default)
835        if sense == 'in' and not 'OUT' in self.attributes:
836            result += " %s *call_%s;\n" % (self.type, self.name)
837
838        return result
839
840    def pre_call(self, method):
841        if 'OUT' in self.attributes or self.sense == 'OUT':
842            return ''
843
844        return """
845if(!%(name)s || (PyObject *)%(name)s==Py_None) {
846   call_%(name)s = NULL;
847} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
848     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
849     goto error;
850} else {
851   call_%(name)s = (%(type)s *)&%(name)s->base;
852};\n""" % self.__dict__
853
854class StructWrapper(Wrapper):
855    """ A wrapper for struct classes """
856    def assign(self, call, method, target = None):
857        args = dict(name = target or self.name, call = call, type = self.type)
858        result = """
859PyErr_Clear();
860%(name)s = (Gen_wrapper *)PyObject_New(py%(type)s, &%(type)s_Type);
861%(name)s->ctx = talloc_size(NULL, 1);
862%(name)s->base = %(call)s;
863""" % args
864
865        if "NULL_OK" in self.attributes:
866            result += "if(!%(name)s->base) { Py_DECREF(%(name)s); return NULL; };" % args
867
868        result += """
869// A NULL object gets translated to a None
870 if(!%(name)s->base) {
871   Py_DECREF(%(name)s);
872   Py_INCREF(Py_None);
873   %(name)s = (Gen_wrapper *)Py_None;
874 } else {
875""" % args
876
877        if "FOREIGN" in self.attributes:
878            result += '// Not taking references to foreign memory\n'
879        elif "BORROWED" in self.attributes:
880            result += "talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
881        else:
882            result += "talloc_steal(%(name)s->ctx, %(name)s->base);\n" % args
883
884        result += "}\n"
885
886        return result
887
888    def byref(self):
889        return "&%s" % self.name
890
891    def definition(self, default = 'NULL', sense='in', **kw):
892        result = "Gen_wrapper *%s = %s;" % (self.name, default)
893        if sense == 'in' and not 'OUT' in self.attributes:
894            result += " %s *call_%s;\n" % (self.type, self.name)
895
896        return result;
897
898class PointerStructWrapper(StructWrapper):
899    def __init__(self, name, type):
900        type = type.split()[0]
901        Wrapper.__init__(self,name, type)
902
903class Timeval(Type):
904    """ handle struct timeval values """
905    interface = 'numeric'
906    buildstr = 'f'
907
908    def definition(self, default = None, **kw):
909        return "float %(name)s_flt; struct timeval %(name)s;\n" % self.__dict__
910
911    def byref(self):
912        return "&%s_flt" % self.name
913
914    def pre_call(self, method):
915        return "%(name)s.tv_sec = (int)%(name)s_flt; %(name)s.tv_usec = (%(name)s_flt - %(name)s.tv_sec) * 1e6;\n" % self.__dict__
916
917    def to_python_object(self, name=None, result = 'py_result', **kw):
918        name = name or self.name
919        return """%(name)s_flt = (double)(%(name)s.tv_sec) + %(name)s.tv_usec;
920%(result)s = PyFloat_FromDouble(%(name)s_flt);
921""" % dict(name = name, result=result)
922
923class PyObject(Type):
924    """ Accept an opaque python object """
925    interface = 'opaque'
926    buildstr = 'O'
927    def definition(self, default = 'NULL', **kw):
928        self.default = default
929        return 'PyObject *%(name)s = %(default)s;\n' % self.__dict__
930
931    def byref(self):
932        return "&%s" % self.name
933
934type_dispatcher = {
935    "IN char *": String,
936    "IN unsigned char *": String,
937    "unsigned char *": String,
938    "char *": String,
939    "ZString": ZString,
940
941    "OUT char *": StringOut,
942    "OUT unsigned char *": StringOut,
943    "unsigned int": Integer,
944    'int': Integer,
945    'OUT uint64_t *': IntegerOut,
946    'OUT uint32_t *': Integer32Out,
947    'char': Char,
948    'void': Void,
949    'void *': Void,
950
951    'TDB_DATA *': TDB_DATA_P,
952    'TDB_DATA': TDB_DATA,
953    'uint64_t': Integer,
954    'uint32_t': Integer32,
955    'time_t': Integer32,
956    'uint16_t': Integer,
957    'uint8_t': Integer,
958    'int64_t': Integer,
959    'ssize_t': Integer,
960    'size_t': Integer,
961    'unsigned long int': Integer,
962    'struct timeval': Timeval,
963    'char **': StringArray,
964
965    'PyObject *': PyObject,
966    }
967
968method_attributes = ['BORROWED', 'DESTRUCTOR','IGNORE']
969
970def dispatch(name, type):
971    if not type: return Void()
972
973    m = re.match("struct ([a-zA-Z0-9]+)_t *", type)
974    if m:
975        type = m.group(1)
976
977    type_components = type.split()
978    attributes = set()
979
980    if type_components[0] in method_attributes:
981        attributes.add(type_components.pop(0))
982
983    type = " ".join(type_components)
984    result = type_dispatcher[type](name, type)
985
986    result.attributes = attributes
987
988    return result
989
990class ResultException:
991    value = 0
992    exception = "PyExc_IOError"
993
994    def __init__(self, check, exception, message):
995        self.check = check
996        self.exception = exception
997        self.message = message
998
999    def write(self, out):
1000        out.write("\n//Handle exceptions\n")
1001        out.write("if(%s) {\n    PyErr_Format(PyExc_%s, %s);\n  goto error; \n};\n\n" % (
1002                self.check, self.exception, self.message))
1003
1004class Method:
1005    default_re = re.compile("DEFAULT\(([A-Z_a-z0-9]+)\) =(.+)")
1006    exception_re = re.compile("RAISES\(([^,]+),\s*([^\)]+)\) =(.+)")
1007    typedefed_re = re.compile(r"struct (.+)_t \*")
1008
1009    def __init__(self, class_name, base_class_name, method_name, args, return_type,
1010                 myclass = None):
1011        self.name = method_name
1012        ## myclass needs to be a class generator
1013        if not isinstance(myclass, ClassGenerator): raise RuntimeError("myclass must be a class generator")
1014
1015        self.myclass = myclass
1016        self.docstring = ''
1017        self.defaults = {}
1018        self.exception = None
1019        self.error_set = False
1020        self.class_name = class_name
1021        self.base_class_name = base_class_name
1022        self.args = []
1023        self.definition_class_name = class_name
1024        for type,name in args:
1025            self.add_arg(type, name)
1026
1027        try:
1028            self.return_type = dispatch('func_return', return_type)
1029            self.return_type.attributes.add("OUT")
1030            self.return_type.original_type = return_type
1031        except KeyError:
1032            ## Is it a wrapped type?
1033            if return_type:
1034                log("Unable to handle return type %s.%s %s" % (self.class_name, self.name, return_type))
1035                #pdb.set_trace()
1036            self.return_type = Void()
1037
1038    def clone(self, new_class_name):
1039        self.find_optional_vars()
1040
1041        result = self.__class__(new_class_name, self.base_class_name, self.name,
1042                                [], 'void *',
1043                                myclass = self.myclass)
1044        result.args = self.args
1045        result.return_type = self.return_type
1046        result.definition_class_name = self.definition_class_name
1047        result.defaults = self.defaults
1048        result.exception = self.exception
1049
1050        return result
1051
1052    def find_optional_vars(self):
1053        for line in self.docstring.splitlines():
1054            m =self.default_re.search(line)
1055            if m:
1056                name = m.group(1)
1057                value = m.group(2)
1058                log("Setting default value for %s of %s" % (m.group(1),
1059                                                            m.group(2)))
1060                self.defaults[name] = value
1061
1062            m =self.exception_re.search(line)
1063            if m:
1064                self.exception = ResultException(m.group(1), m.group(2), m.group(3))
1065
1066    def write_local_vars(self, out):
1067        self.find_optional_vars()
1068
1069        ## We do it in two passes - first mandatory then optional
1070        kwlist = """static char *kwlist[] = {"""
1071        ## Mandatory
1072        for type in self.args:
1073            python_name = type.python_name()
1074            if python_name and python_name not in self.defaults:
1075                kwlist += '"%s",' % python_name
1076
1077        for type in self.args:
1078            python_name = type.python_name()
1079            if python_name and python_name in self.defaults:
1080                kwlist += '"%s",' % python_name
1081
1082        kwlist += ' NULL};\n'
1083
1084        for type in self.args:
1085            python_name = type.python_name()
1086            try:
1087                out.write(type.definition(default = self.defaults[python_name]))
1088            except KeyError:
1089                out.write(type.definition())
1090
1091        ## Make up the format string for the parse args in two pases
1092        parse_line = ''
1093        for type in self.args:
1094            python_name = type.python_name()
1095            if type.buildstr and python_name not in self.defaults:
1096                parse_line += type.buildstr
1097
1098        parse_line += '|'
1099        for type in self.args:
1100            python_name = type.python_name()
1101            if type.buildstr and python_name in self.defaults:
1102                parse_line += type.buildstr
1103
1104        if parse_line != '|':
1105            ## Now parse the args from python objects
1106            out.write(kwlist)
1107            out.write("\nif(!PyArg_ParseTupleAndKeywords(args, kwds, \"%s\", kwlist, " % parse_line)
1108            tmp = []
1109            for type in self.args:
1110                ref = type.byref()
1111                if ref:
1112                    tmp.append(ref)
1113
1114            out.write(",".join(tmp))
1115            self.error_set = True
1116            out.write("))\n goto error;\n\n")
1117
1118    def error_condition(self):
1119        result = ""
1120        if "DESTRUCTOR" in self.return_type.attributes:
1121            result += "talloc_free(self->ctx); self->base = NULL;\n"
1122
1123        return result +"return NULL;\n";
1124
1125    def write_definition(self, out):
1126        args = dict(method = self.name, class_name = self.class_name)
1127        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
1128        out.write(self.comment())
1129        out.write("********************************************************/\n")
1130
1131        self._prototype(out)
1132        out.write("""{
1133       PyObject *returned_result, *py_result;
1134""" % args)
1135
1136        out.write(self.return_type.definition())
1137
1138        self.write_local_vars( out);
1139
1140        out.write("""// Make sure that we have something valid to wrap
1141if(!self->base) return PyErr_Format(PyExc_RuntimeError, "%(class_name)s object no longer valid");
1142""" % args)
1143
1144        ## Precall preparations
1145        out.write("// Precall preparations\n")
1146        out.write(self.return_type.pre_call(self))
1147        for type in self.args:
1148            out.write(type.pre_call(self))
1149
1150        out.write("""// Check the function is implemented
1151  {  void *method = ((%(def_class_name)s)self->base)->%(method)s;
1152     if(!method || (void *)unimplemented == (void *)method) {
1153         PyErr_Format(PyExc_RuntimeError, "%(class_name)s.%(method)s is not implemented");
1154         goto error;
1155     };
1156  };
1157""" % dict(def_class_name = self.definition_class_name, method=self.name,
1158           class_name = self.class_name))
1159
1160        out.write("\n// Make the call\n ClearError();")
1161        call = "((%s)self->base)->%s(((%s)self->base)" % (self.definition_class_name, self.name, self.definition_class_name)
1162        tmp = ''
1163        for type in self.args:
1164            tmp += ", " + type.call_arg()
1165
1166        call += "%s)" % tmp
1167
1168        ## Now call the wrapped function
1169        out.write(self.return_type.assign(call, self))
1170        if self.exception:
1171            self.exception.write(out)
1172
1173        self.error_set = True
1174
1175        out.write("\n// Postcall preparations\n")
1176        ## Postcall preparations
1177        out.write(self.return_type.post_call(self))
1178        for type in self.args:
1179            out.write(type.post_call(self))
1180
1181        ## Now assemble the results
1182        results = [self.return_type.to_python_object()]
1183        for type in self.args:
1184            if type.sense == 'OUT_DONE':
1185                results.append(type.to_python_object(results = results))
1186
1187        ## If all the results are returned by reference we dont need
1188        ## to prepend the void return value at all.
1189        if isinstance(self.return_type, Void) and len(results)>1:
1190            results.pop(0)
1191
1192        out.write("\n// prepare results\n")
1193        ## Make a tuple of results and pass them back
1194        if len(results)>1:
1195            out.write("returned_result = PyList_New(0);\n")
1196            for result in results:
1197                out.write(result)
1198                out.write("PyList_Append(returned_result, py_result); Py_DECREF(py_result);\n");
1199            out.write("return returned_result;\n")
1200        else:
1201            out.write(results[0])
1202            ## This useless code removes compiler warnings
1203            out.write("returned_result = py_result;\nreturn returned_result;\n");
1204
1205        ## Write the error part of the function
1206        if self.error_set:
1207            out.write("\n// error conditions:\n")
1208            out.write("error:\n    " + self.error_condition());
1209
1210        out.write("\n};\n\n")
1211
1212    def add_arg(self, type, name):
1213        try:
1214            t = type_dispatcher[type](name, type)
1215        except KeyError:
1216            ## Sometimes types must be typedefed in advance
1217            try:
1218                m = self.typedefed_re.match(type)
1219                type = m.group(1)
1220                log( "Trying %s for %s" % (type, m.group(0)))
1221                t = type_dispatcher[type](name, type)
1222            except (KeyError, AttributeError):
1223                log( "Unable to handle type %s.%s %s" % (self.class_name, self.name, type))
1224                return
1225
1226        ## Here we collapse char * + int type interfaces into a
1227        ## coherent string like interface.
1228        try:
1229            previous = self.args[-1]
1230            if t.interface == 'integer' and \
1231                    previous.interface == 'string':
1232
1233                ## We make a distinction between IN variables and OUT
1234                ## variables
1235                if previous.sense == 'OUT':
1236                    cls = Char_and_Length_OUT
1237                else:
1238                    cls = Char_and_Length
1239
1240
1241                cls = cls(
1242                    previous.name,
1243                    previous.type,
1244                    name, type)
1245
1246                self.args[-1] = cls
1247
1248                return
1249        except IndexError:
1250            pass
1251
1252        self.args.append(t)
1253
1254    def comment(self):
1255        result = self.return_type.original_type+" "+self.class_name+"."+self.name+"("
1256        args = []
1257        for type in self.args:
1258            args.append( type.comment())
1259
1260        result += ",".join(args) + ");\n"
1261
1262        return result
1263
1264    def prototype(self, out):
1265        self._prototype(out)
1266        out.write(";\n")
1267
1268    def _prototype(self, out):
1269        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name))
1270
1271    def __str__(self):
1272        result = "def %s %s(%s):" % (
1273            self.return_type,
1274            self.name, ' , '.join([a.__str__() for a in self.args]))
1275        return result
1276
1277    def PyMethodDef(self, out):
1278        docstring = self.comment() + "\n\n" + self.docstring
1279        out.write('     {"%s",(PyCFunction)py%s_%s, METH_VARARGS|METH_KEYWORDS, "%s"},\n' % (
1280                self.name,
1281                self.class_name,
1282                self.name, escape_for_string(docstring)))
1283
1284class IteratorMethod(Method):
1285    """ A Method which implements an iterator """
1286    def __init__(self, *args, **kw):
1287        Method.__init__(self, *args, **kw)
1288
1289        ## Tell the return type that a NULL python return is ok
1290        self.return_type.attributes.add("NULL_OK")
1291
1292    def _prototype(self, out):
1293        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self)""" % dict(method = self.name, class_name = self.class_name))
1294
1295    def __str__(self):
1296        result = "Iterator returning %s." % (
1297            self.return_type)
1298        return result
1299
1300    def PyMethodDef(self, out):
1301        ## This method should not go in the method table as its linked
1302        ## in directly
1303        pass
1304
1305class SelfIteratorMethod(IteratorMethod):
1306    def write_definition(self, out):
1307        args = dict(method = self.name, class_name = self.class_name)
1308        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
1309        out.write(self.comment())
1310        out.write("********************************************************/\n")
1311
1312        self._prototype(out)
1313        out.write("""{
1314          ((%(class_name)s)self->base)->%(method)s((%(class_name)s)self->base);
1315          return PyObject_SelfIter((PyObject *)self);
1316};
1317""" % args)
1318
1319class ConstructorMethod(Method):
1320    ## Python constructors are a bit different than regular methods
1321    def _prototype(self, out):
1322        out.write("""
1323static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds)
1324""" % dict(method = self.name, class_name = self.class_name))
1325
1326    def write_destructor(self, out):
1327        ## We make sure that we unlink exactly the reference we need
1328        ## (the object will persist if it has some other
1329        ## references). Note that if we just used talloc_free here it
1330        ## will remove some random reference which may not actually be
1331        ## the reference we own (which is NULL).
1332        free = """
1333    if(self->base) {
1334        //printf("Unlinking %s@%p\\n", NAMEOF(self->base), self->base);
1335        talloc_free(self->ctx);
1336        self->base=NULL;
1337    };
1338"""
1339        out.write("""static void
1340%(class_name)s_dealloc(py%(class_name)s *self) {
1341%(free)s
1342 PyObject_Del(self);
1343};\n
1344""" % dict(class_name = self.class_name, free=free))
1345
1346    def error_condition(self):
1347        return "return -1;";
1348
1349    def write_definition(self, out):
1350        self._prototype(out)
1351        out.write("""{\n""")
1352        self.write_local_vars(out)
1353
1354        ## Precall preparations
1355        for type in self.args:
1356            out.write(type.pre_call(self))
1357
1358        ## Now call the wrapped function
1359        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
1360        out.write("\n ClearError();\nPy_BEGIN_ALLOW_THREADS\nself->base = CONSTRUCT(%s, %s, %s, self->ctx" % (
1361                self.class_name,
1362                self.definition_class_name,
1363                self.name))
1364        tmp = ''
1365        for type in self.args:
1366            tmp += ", " + type.call_arg()
1367
1368        self.error_set = True
1369        out.write("""%s);\nPy_END_ALLOW_THREADS\n
1370       if(!CheckError(EZero)) {
1371         char *buffer;
1372         PyObject *exception = resolve_exception(&buffer);
1373
1374         PyErr_Format(exception,
1375                    "%%s", buffer);
1376         ClearError();
1377         goto error;
1378  } else if(!self->base) {
1379    PyErr_Format(PyExc_IOError, "Unable to construct class %s");
1380    goto error;
1381  };
1382""" % (tmp, self.class_name))
1383
1384        out.write("  return 0;\n");
1385
1386        ## Write the error part of the function
1387        if self.error_set:
1388            out.write("error:\n    " + self.error_condition());
1389
1390        out.write("\n};\n\n")
1391
1392class GetattrMethod(Method):
1393    def __init__(self, class_name, base_class_name, myclass):
1394        self.base_class_name = base_class_name
1395        self._attributes = []
1396        self.error_set = True
1397        self.return_type = Void()
1398        self.myclass = myclass
1399        self.rename_class_name(class_name)
1400
1401    def add_attribute(self, attr):
1402        if attr.name:
1403            self._attributes.append([self.class_name, attr])
1404
1405    def rename_class_name(self, new_name):
1406        """ This allows us to rename the class_name at a later stage.
1407        Required for late initialization of Structs whose name is not
1408        know until much later on.
1409        """
1410        self.class_name = new_name
1411        self.name = "py%s_getattr" % new_name
1412        for x in self._attributes:
1413            x[0] = new_name
1414
1415    def get_attributes(self):
1416        for class_name, attr in self._attributes:
1417            try:
1418                if not self.myclass.module.classes[attr.type].active:
1419                    continue
1420            except KeyError: pass
1421
1422            yield class_name, attr
1423
1424    def __str__(self):
1425        result = ""
1426        for class_name, attr in self.get_attributes():
1427            result += "    %s\n" % attr.__str__()
1428
1429        return result
1430
1431
1432    def clone(self, class_name):
1433        result = self.__class__(class_name, self.base_class_name, self.myclass)
1434        result._attributes = self._attributes[:]
1435
1436        return result
1437
1438    def prototype(self, out):
1439        if self.name:
1440            out.write("""
1441static PyObject *%(name)s(py%(class_name)s *self, PyObject *name);
1442""" % self.__dict__)
1443
1444    def built_ins(self, out):
1445        """ check for some built in attributes we need to support """
1446        out.write("""  if(!strcmp(name, "__members__")) {
1447     PyObject *result = PyList_New(0);
1448     PyObject *tmp;
1449     PyMethodDef *i;
1450
1451     if(!result) goto error;
1452""")
1453        ## Add attributes
1454        for class_name, attr in self.get_attributes():
1455            out.write(""" tmp = PyString_FromString("%(name)s");
1456    PyList_Append(result, tmp); Py_DECREF(tmp);
1457""" % dict(name = attr.name))
1458
1459        ## Add methods
1460        out.write("""
1461
1462    for(i=%s_methods; i->ml_name; i++) {
1463     tmp = PyString_FromString(i->ml_name);
1464    PyList_Append(result, tmp); Py_DECREF(tmp);
1465    }; """ % self.class_name)
1466
1467        out.write("""
1468     return result;
1469   }\n""")
1470
1471    def write_definition(self, out):
1472        if not self.name: return
1473        out.write("""
1474static PyObject *py%(class_name)s_getattr(py%(class_name)s *self, PyObject *pyname) {
1475  char *name;
1476  // Try to hand it off to the python native handler first
1477  PyObject *result = PyObject_GenericGetAttr((PyObject*)self, pyname);
1478
1479  if(result) return result;
1480
1481  PyErr_Clear();
1482  // No - nothing interesting was found by python
1483  name = PyString_AsString(pyname);
1484
1485  if(!self->base) return PyErr_Format(PyExc_RuntimeError, "Wrapped object (%(class_name)s.%(name)s) no longer valid");
1486  if(!name) return NULL;
1487""" % self.__dict__)
1488
1489        self.built_ins(out)
1490
1491        for class_name, attr in self.get_attributes():
1492            ## what we want to assign
1493            if self.base_class_name:
1494                call = "(((%s)self->base)->%s)" % (class_name, attr.name)
1495            else:
1496                call = "(self->base->%s)" % (attr.name)
1497
1498            out.write("""
1499if(!strcmp(name, "%(name)s")) {
1500    PyObject *py_result;
1501    %(python_def)s
1502
1503    %(python_assign)s
1504    %(python_obj)s
1505    return py_result;
1506};""" % dict(name = attr.name, python_obj = attr.to_python_object(),
1507             python_assign = attr.assign(call, self),
1508             python_def = attr.definition(sense='out')))
1509
1510        out.write("""
1511
1512  return PyObject_GenericGetAttr((PyObject *)self, pyname);
1513""" % self.__dict__)
1514
1515        ## Write the error part of the function
1516        if self.error_set:
1517            out.write("error:\n" + self.error_condition());
1518
1519        out.write("}\n\n")
1520
1521class ProxiedGetattr(GetattrMethod):
1522    def built_ins(self,out):
1523        out.write("""  if(!strcmp(name, "__members__")) {
1524     PyObject *result;
1525     PyObject *tmp;
1526     PyMethodDef *i;
1527
1528     PyErr_Clear();
1529     // Get the list of members from our proxied object
1530     result  = PyObject_GetAttrString(self->base->proxied, name);
1531     if(!result) goto error;
1532""")
1533        ## Add attributes
1534        for class_name, attr in self.get_attributes():
1535            out.write(""" tmp = PyString_FromString("%(name)s");
1536    PyList_Append(result, tmp); Py_DECREF(tmp);
1537""" % dict(name = attr.name))
1538
1539        ## Add methods
1540        out.write("""
1541
1542    for(i=%s_methods; i->ml_name; i++) {
1543     tmp = PyString_FromString(i->ml_name);
1544    PyList_Append(result, tmp); Py_DECREF(tmp);
1545    }; """ % self.class_name)
1546
1547        out.write("""
1548     return result;
1549   }\n""")
1550
1551        out.write(""" /** Just try to get the attribute from our proxied object */  {
1552   PyObject *result = PyObject_GetAttrString(self->base->proxied, name);
1553   if(result) return result;
1554}; """)
1555
1556class ProxiedMethod(Method):
1557    def __init__(self, method, myclass):
1558        self.name = method.name
1559        self.method = method
1560        self.myclass = myclass
1561        self.class_name = method.class_name
1562        self.base_class_name = method.base_class_name
1563        self.args = method.args
1564        self.definition_class_name = method.definition_class_name
1565        self.return_type = method.return_type
1566        self.docstring = "Proxy for %s" % self.name
1567        self.defaults = {}
1568        self.exception = None
1569        self.error_set = False
1570
1571    def get_name(self):
1572        return "py%(class_name)s_%(name)s" % dict(class_name =self.myclass.class_name,
1573                                                  name = self.name)
1574
1575    def _prototype(self, out):
1576        out.write("""
1577static %(return_type)s %(name)s(%(definition_class_name)s self""" % dict(
1578                return_type = self.return_type.original_type,
1579                class_name = self.myclass.class_name,
1580                method = self.name,
1581                name = self.get_name(),
1582                definition_class_name = self.definition_class_name))
1583
1584        for arg in self.args:
1585            out.write(", %s" % (arg.comment()))
1586
1587        out.write(")")
1588
1589    def prototype(self, out):
1590        self._prototype(out)
1591        out.write(";\n")
1592
1593    def write_definition(self, out):
1594        self._prototype(out)
1595        self._write_definition(out)
1596
1597    def _write_definition(self, out):
1598        ## We need to grab the GIL before we do anything
1599        out.write("""{
1600      //Grab the GIL so we can do python stuff
1601      PyGILState_STATE gstate;
1602      gstate = PyGILState_Ensure();
1603      """)
1604
1605        out.write("{\nPyObject *py_result=NULL;\n")
1606        out.write('PyObject *method_name = PyString_FromString("%s");\n' % self.name)
1607        out.write(self.return_type.returned_python_definition())
1608
1609        for arg in self.args:
1610            out.write("PyObject *py_%s=NULL;\n" % arg.name)
1611
1612        out.write("\n//Obtain python objects for all the args:\n")
1613        for arg in self.args:
1614            out.write(arg.to_python_object(result = "py_%s" % arg.name,
1615                                           sense='proxied', BORROWED=True))
1616
1617        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n};\n' % (self.myclass.class_name, self.myclass.class_name))
1618
1619        out.write("\n//Now call the method\n")
1620        out.write("""PyErr_Clear();
1621py_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name,""" % self.myclass.class_name)
1622        for arg in self.args:
1623            out.write("py_%s," % arg.name)
1624
1625        ## Sentinal
1626        self.error_set = True
1627        out.write("""NULL);
1628
1629/** Check for python errors */
1630if(PyErr_Occurred()) {
1631   PyObject *exception_t, *exception, *tb;
1632   PyObject *str;
1633   char *str_c;
1634   char *error_str;
1635   enum _error_type *error_type = aff4_get_current_error(&error_str);
1636
1637   // Fetch the exception state and convert it to a string:
1638   PyErr_Fetch(&exception_t, &exception, &tb);
1639
1640   str = PyObject_Repr(exception);
1641   str_c = PyString_AsString(str);
1642
1643   if(str_c) {
1644      strncpy(error_str, str_c, BUFF_SIZE-1);
1645      error_str[BUFF_SIZE-1]=0;
1646      *error_type = ERuntimeError;
1647   };
1648   Py_DECREF(str);
1649   goto error;
1650};
1651
1652""");
1653
1654        for arg in self.args:
1655            out.write(arg.python_proxy_post_call())
1656
1657        ## Now convert the python value back to a value
1658        out.write(self.return_type.from_python_object('py_result',self.return_type.name, self, context = "self"))
1659
1660        out.write("if(py_result) { Py_DECREF(py_result);};\nPy_DECREF(method_name);\n\n");
1661        out.write("PyGILState_Release(gstate);\n")
1662
1663        ## Decref all our python objects:
1664        for arg in self.args:
1665            out.write("if(py_%s) { Py_DECREF(py_%s);};\n" %( arg.name, arg.name))
1666
1667        out.write(self.return_type.return_value('func_return'))
1668        if self.error_set:
1669            out.write("\nerror:\n")
1670            out.write("if(py_result) { Py_DECREF(py_result);};\nPy_DECREF(method_name);\n\n");
1671            ## Decref all our python objects:
1672            for arg in self.args:
1673                out.write("if(py_%s) { Py_DECREF(py_%s);};\n" % (arg.name, arg.name))
1674
1675            out.write("PyGILState_Release(gstate);\n %s;\n" % self.error_condition())
1676
1677        out.write("   };\n};\n")
1678
1679    def error_condition(self):
1680        return self.return_type.error_value
1681
1682class StructConstructor(ConstructorMethod):
1683    """ A constructor for struct wrappers - basically just allocate
1684    memory for the struct.
1685    """
1686    def write_definition(self, out):
1687        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
1688        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
1689        out.write("\nself->base = talloc(self->ctx, %s);\n" % self.class_name)
1690        out.write("  return 0;\n};\n\n")
1691
1692    def write_destructor(self, out):
1693        out.write("""static void
1694%(class_name)s_dealloc(py%(class_name)s *self) {
1695   talloc_free(self->ctx);
1696};\n
1697""" % dict(class_name = self.class_name))
1698
1699class ProxyConstructor(ConstructorMethod):
1700    def write_destructor(self, out):
1701        out.write("""static void
1702%(class_name)s_dealloc(py%(class_name)s *self) {
1703    if(self->base) {
1704        // Release the proxied object
1705        //Py_DECREF(self->base->proxied);
1706        talloc_free(self->ctx);
1707        self->base = NULL;
1708    };
1709};\n
1710
1711static int %(class_name)s_destructor(void *this) {
1712  py%(class_name)s *self = (py%(class_name)s *)this;
1713  Py_DECREF(self->base->proxied);
1714  return 0;
1715};
1716""" % dict(class_name = self.class_name))
1717
1718    def initialise_attributes(self, out):
1719        attributes = self.myclass.module.classes[self.base_class_name].attributes.get_attributes()
1720        for definition_class_name, attribute in attributes:
1721            out.write("""
1722{
1723  // Converting from %(attribute_name)s
1724  PyErr_Clear();
1725  PyObject *py_result = PyObject_GetAttrString(self->base->proxied, "%(name)s");
1726
1727  if(py_result) {
1728       %(type)s tmp;
1729       %(from_python_object)s;
1730       ((%(definition_class_name)s)self->base)->%(name)s = tmp;
1731       Py_DECREF(py_result);
1732  };
1733  PyErr_Clear();
1734};""" % dict(definition = attribute.definition(), name=attribute.name,
1735             attribute_name = attribute.__class__.__name__,
1736             type = attribute.type,
1737             definition_class_name = definition_class_name,
1738             from_python_object = attribute.from_python_object(
1739                        'py_result',"tmp", method=self,
1740                        context = 'self->base')))
1741
1742
1743    def write_constructor_proxy(self, out):
1744        ## Get the base_class constructor
1745        self.base_cons_method = ProxiedMethod(self.myclass.module.classes[self.base_class_name].constructor, self.myclass)
1746
1747        self.base_cons_method._prototype(out)
1748        out.write("{\nPyObject *py_result;\n")
1749        out.write('PyObject *method_name;')
1750        out.write("%(class_name)s this = (%(class_name)s)self;\n" % self.__dict__)
1751        out.write("PyGILState_STATE gstate;\ngstate = PyGILState_Ensure();\n")
1752        out.write('method_name = PyString_FromString("__class__");\n')
1753        for arg in self.base_cons_method.args:
1754            out.write("PyObject *py_%s;\n" % arg.name)
1755
1756        out.write("\n//Obtain python objects for all the args:\n")
1757        for arg in self.base_cons_method.args:
1758            out.write(arg.to_python_object(result = "py_%s" % arg.name,
1759                                           sense = 'proxied',
1760                                           BORROWED=True))
1761
1762        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n};\n' % (self.myclass.class_name, self.myclass.class_name))
1763
1764        out.write("""
1765// Enlarge the object size to accomodate the extended class
1766self = talloc_realloc_size(self, self, sizeof(struct %(base_class_name)s_t));
1767""" % self.__dict__)
1768        out.write("\n//Now call the method\n")
1769        out.write("PyErr_Clear();\npy_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name," % self.myclass.class_name)
1770
1771        call = ''
1772        for arg in self.base_cons_method.args:
1773            call += "py_%s," % arg.name
1774
1775        ## Sentinal
1776        self.error_set = True
1777        call += """NULL"""
1778
1779        out.write(call + ");\n");
1780        out.write("""
1781if(!py_result && PyCallable_Check(this->proxied)) {
1782   PyErr_Clear();
1783   py_result = PyObject_CallFunctionObjArgs(((%(name)s)self)->proxied, %(call)s);
1784};
1785
1786/** Check for python errors */
1787if(PyErr_Occurred()) {
1788   PyObject *exception_t, *exception, *tb;
1789   PyObject *str;
1790   char *str_c;
1791   char *error_str;
1792   enum _error_type *error_type = aff4_get_current_error(&error_str);
1793
1794   // Fetch the exception state and convert it to a string:
1795   PyErr_Fetch(&exception_t, &exception, &tb);
1796
1797   str = PyObject_Repr(exception);
1798   str_c = PyString_AsString(str);
1799
1800   if(str_c) {
1801      strncpy(error_str, str_c, BUFF_SIZE-1);
1802      error_str[BUFF_SIZE-1]=0;
1803      *error_type = ERuntimeError;
1804   };
1805   Py_DECREF(str);
1806   goto error;
1807};
1808
1809// Take over the proxied object now
1810this->proxied = py_result;
1811""" % dict(name=self.myclass.class_name, call = call));
1812
1813        ## Now we try to populate the C struct slots with proxies of
1814        ## the python objects
1815        for class_name, attr in self.myclass.module.classes[\
1816            self.base_class_name].attributes.get_attributes():
1817            out.write("""
1818// Converting %(name)s from proxy:
1819{
1820   PyObject *py_result = PyObject_GetAttrString(this->proxied, "%(name)s");
1821   if(py_result) {
1822""" % dict(name = attr.name))
1823            out.write("{ %s " % attr.definition())
1824            out.write(attr.from_python_object("py_result",
1825                                              "((%s)self)->%s" % (class_name, attr.name),
1826                                              self))
1827            out.write("}\nPy_DECREF(py_result);\n};\n};\n")
1828
1829        out.write("PyGILState_Release(gstate);\n")
1830        out.write("\n\nreturn self;\n")
1831        if self.error_set:
1832            out.write("error:\n PyGILState_Release(gstate);\ntalloc_free(self); return NULL;\n")
1833
1834        out.write("};\n\n")
1835
1836    def write_definition(self, out):
1837        self.write_constructor_proxy(out)
1838        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {
1839      PyGILState_STATE gstate = PyGILState_Ensure();
1840""" % dict(method = self.name, class_name = self.class_name))
1841
1842        self.write_local_vars(out)
1843
1844        ## Precall preparations
1845        for type in self.args:
1846            out.write(type.pre_call(self))
1847
1848        ## Make up the call
1849        self.call = "talloc_memdup(NULL, &__%(class_name)s, sizeof(__%(class_name)s));\n" % self.__dict__
1850
1851        ## Now call the wrapped function
1852        out.write("""
1853 self->base = %(call)s
1854
1855// Take over a copy of the proxied object
1856 self->base->proxied = proxied;
1857
1858 /* We take a reference to the proxied object, and the proxied base
1859 class takes a reference. This way we (the python object) and the
1860 proxied C class can be freed independantly and only when both are
1861 freed the proxied object is freed.  */
1862
1863 //Py_INCREF(proxied);
1864 Py_INCREF(proxied);
1865 talloc_set_destructor((void*)self->base, %(class_name)s_destructor);
1866""" % self.__dict__)
1867
1868        ## Install the handler for the constructor. FIXME - implement
1869        ## shortcut bypassing here so if the proxied class is itself a
1870        ## python binding to a C class and it does not override
1871        ## anything, we call directly into the original C class method
1872        ## instead of converting to python, and back.
1873        out.write("((%(definition_class_name)s)self->base)->%(name)s = %(func)s;\n" % dict(
1874                definition_class_name = self.base_cons_method.definition_class_name,
1875                name = self.base_cons_method.name,
1876                func = self.base_cons_method.get_name()))
1877
1878        ## Now iterate over all our methods and install handlers:
1879        for method in self.myclass.methods:
1880            out.write("""
1881   if(1 || PyDict_GetItemString(proxied->ob_type->tp_dict, "%(name)s")) {
1882     ((%(definition_class_name)s)self->base)->%(name)s = py%(class_name)s_%(name)s;
1883   };
1884""" % dict(definition_class_name = method.definition_class_name,
1885           name = method.name,
1886           class_name = self.myclass.class_name))
1887
1888        ## Now fill in all attributes from the proxied object. Since
1889        ## the C struct attribute access is just memory access its
1890        ## difficult to trap it and refill attributes dynamically from
1891        ## the python object. Therefore for now we just read all
1892        ## attributes initially and populate the C struct with them.
1893        self.initialise_attributes(out)
1894
1895        out.write("\n   PyGILState_Release(gstate);\n  return 0;\n");
1896
1897        ## Write the error part of the function
1898        if self.error_set:
1899            out.write("error:\n  PyGILState_Release(gstate);\n  " + self.error_condition());
1900
1901        out.write("\n};\n\n")
1902
1903class EmptyConstructor(ConstructorMethod):
1904    def write_definition(self, out):
1905        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
1906        out.write("""return 0;};\n\n""")
1907
1908class ClassGenerator:
1909    docstring = ''
1910    def __init__(self, class_name, base_class_name, module):
1911        self.class_name = class_name
1912        self.methods = []
1913        self.module = module
1914        self.constructor = EmptyConstructor(class_name, base_class_name,
1915                                             "Con", [], '', myclass = self)
1916
1917        self.base_class_name = base_class_name
1918        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
1919        self.modifier = set()
1920        self.active = True
1921        self.iterator = None
1922
1923    def prepare(self):
1924        """ This method is called just before we need to write the
1925        output and allows us to do any last minute fixups.
1926        """
1927        pass
1928
1929    def __str__(self):
1930        result = "#%s\n" % self.docstring
1931
1932        result += "Class %s(%s):\n" % (self.class_name, self.base_class_name)
1933        result += " Constructor:%s\n" % self.constructor
1934        result += " Attributes:\n%s\n" % self.attributes
1935        result += " Methods:\n"
1936        for a in self.methods:
1937            result += "    %s\n" % a.__str__()
1938
1939        return result
1940
1941    def is_active(self):
1942        """ Returns true if this class is active and should be generated """
1943        if not self.active or self.modifier and \
1944                ('PRIVATE' in self.modifier or 'ABSTRACT' in self.modifier):
1945            log("%s is not active %s" % (self.class_name, self.modifier))
1946            return False
1947
1948        return True
1949
1950    def clone(self, new_class_name):
1951        """ Creates a clone of this class - usefull when implementing
1952        class extensions
1953        """
1954        result = ClassGenerator(new_class_name, self.class_name, self.module)
1955        result.constructor = self.constructor.clone(new_class_name)
1956        result.methods = [ x.clone(new_class_name) for x in self.methods ]
1957        result.attributes = self.attributes.clone(new_class_name)
1958
1959        return result
1960
1961    def add_attribute(self, attr_name, attr_type, modifier):
1962        try:
1963            if not self.module.classes[attr_type].is_active(): return
1964        except KeyError: pass
1965
1966        try:
1967            ## All attribute references are always borrowed - that
1968            ## means we dont want to free them after accessing them
1969            type_class = dispatch(attr_name, "BORROWED "+attr_type)
1970        except KeyError:
1971            log("Unknown attribute type %s for  %s.%s" % (attr_type,
1972                                                          self.class_name,
1973                                                          attr_name))
1974            return
1975
1976        type_class.attributes.add(modifier)
1977        self.attributes.add_attribute(type_class)
1978
1979    def add_constructor(self, method_name, args, return_type, docstring):
1980        if method_name.startswith("Con"):
1981            self.constructor = ConstructorMethod(self.class_name, self.base_class_name,
1982                                                 method_name, args, return_type,
1983                                                 myclass = self)
1984            self.constructor.docstring = docstring
1985
1986    def struct(self,out):
1987        out.write("""\ntypedef struct {
1988  PyObject_HEAD
1989  %(class_name)s base;
1990  void *ctx;
1991} py%(class_name)s;\n
1992""" % dict(class_name=self.class_name))
1993
1994    def code(self, out):
1995        if not self.constructor:
1996            raise RuntimeError("No constructor found for class %s" % self.class_name)
1997
1998        self.constructor.write_destructor(out)
1999        self.constructor.write_definition(out)
2000        if self.attributes:
2001            self.attributes.write_definition(out)
2002
2003        for m in self.methods:
2004            m.write_definition(out)
2005
2006    def initialise(self):
2007        return "python_wrappers[TOTAL_CLASSES].class_ref = (Object)&__%s;\n" \
2008            "python_wrappers[TOTAL_CLASSES++].python_type = &%s_Type;\n" % (
2009            self.class_name, self.class_name)
2010
2011    def PyMethodDef(self, out):
2012        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
2013        for method in self.methods:
2014            method.PyMethodDef(out)
2015
2016        out.write("     {NULL}  /* Sentinel */\n};\n")
2017
2018    def prototypes(self, out):
2019        """ Write prototype suitable for .h file """
2020        out.write("""staticforward PyTypeObject %s_Type;\n""" % self.class_name)
2021        self.constructor.prototype(out)
2022
2023        if self.attributes:
2024            self.attributes.prototype(out)
2025        for method in self.methods:
2026            method.prototype(out)
2027
2028    def numeric_protocol_int(self):
2029        pass
2030
2031    def numeric_protocol_nonzero(self):
2032        return """
2033static int
2034%(class_name)s_nonzero(py%(class_name)s *v)
2035{
2036        return v->base != 0;
2037};
2038""" % self.__dict__
2039
2040    def numeric_protocol(self, out):
2041        args = {'class':self.class_name}
2042        for type, func in [ ('nonzero', self.numeric_protocol_nonzero),
2043                            ('int', self.numeric_protocol_int) ]:
2044            definition = func()
2045            if definition:
2046                out.write(definition)
2047                args[type] = "%s_%s" % (self.class_name,type)
2048            else:
2049                args[type] = '0'
2050
2051        out.write("""
2052static PyNumberMethods %(class)s_as_number = {
2053        (binaryfunc)    0,       /*nb_add*/
2054        (binaryfunc)    0,       /*nb_subtract*/
2055        (binaryfunc)    0,       /*nb_multiply*/
2056                        0,       /*nb_divide*/
2057                        0,       /*nb_remainder*/
2058                        0,       /*nb_divmod*/
2059                        0,       /*nb_power*/
2060        (unaryfunc)     0,       /*nb_negative*/
2061        (unaryfunc)     0,       /*tp_positive*/
2062        (unaryfunc)     0,       /*tp_absolute*/
2063        (inquiry)       %(nonzero)s,   /*tp_nonzero*/
2064        (unaryfunc)     0,       /*nb_invert*/
2065                        0,       /*nb_lshift*/
2066        (binaryfunc)    0,       /*nb_rshift*/
2067                        0,       /*nb_and*/
2068                        0,       /*nb_xor*/
2069                        0,       /*nb_or*/
2070                        0,       /*nb_coerce*/
2071         (unaryfunc)    %(int)s,       /*nb_int*/
2072                        0,       /*nb_long*/
2073                        0,       /*nb_float*/
2074                        0,       /*nb_oct*/
2075                        0,       /*nb_hex*/
2076        0,                              /* nb_inplace_add */
2077        0,                              /* nb_inplace_subtract */
2078        0,                              /* nb_inplace_multiply */
2079        0,                              /* nb_inplace_divide */
2080        0,                              /* nb_inplace_remainder */
2081        0,                              /* nb_inplace_power */
2082        0,                              /* nb_inplace_lshift */
2083        0,                              /* nb_inplace_rshift */
2084        0,                              /* nb_inplace_and */
2085        0,                              /* nb_inplace_xor */
2086        0,                              /* nb_inplace_or */
2087        0,                              /* nb_floor_divide */
2088        0,                              /* nb_true_divide */
2089        0,                              /* nb_inplace_floor_divide */
2090        0,                              /* nb_inplace_true_divide */
2091        0,                              /* nb_index */
2092};
2093"""  % args)
2094        return "&%(class)s_as_number" % args
2095
2096    def PyTypeObject(self, out):
2097        args = {'class':self.class_name, 'module': self.module.name, 
2098                'iterator': 0,
2099                'iternext': 0,
2100                'tp_str': 0,
2101                'getattr_func': 0,
2102                'docstring': "%s: %s" % (self.class_name, 
2103                                         escape_for_string(self.docstring))}
2104
2105        if self.attributes:
2106            args['getattr_func'] = self.attributes.name
2107
2108        args['numeric_protocol'] = self.numeric_protocol(out)
2109        if "ITERATOR" in self.modifier:
2110            args['iterator'] = "PyObject_SelfIter"
2111            args['iternext'] = "py%s_iternext" % self.class_name
2112
2113        if "SELF_ITER" in self.modifier:
2114            args['iterator'] = 'py%s___iter__' % self.class_name
2115
2116        if "TP_STR" in self.modifier:
2117            args['tp_str'] = 'py%s___str__' % self.class_name
2118
2119        out.write("""
2120static PyTypeObject %(class)s_Type = {
2121    PyObject_HEAD_INIT(NULL)
2122    0,                         /* ob_size */
2123    "%(module)s.%(class)s",               /* tp_name */
2124    sizeof(py%(class)s),            /* tp_basicsize */
2125    0,                         /* tp_itemsize */
2126    (destructor)%(class)s_dealloc,/* tp_dealloc */
2127    0,                         /* tp_print */
2128    0,                         /* tp_getattr */
2129    0,                         /* tp_setattr */
2130    0,                         /* tp_compare */
2131    0,                         /* tp_repr */
2132    %(numeric_protocol)s,      /* tp_as_number */
2133    0,                         /* tp_as_sequence */
2134    0,                         /* tp_as_mapping */
2135    0,                         /* tp_hash */
2136    0,                         /* tp_call */
2137    (reprfunc)%(tp_str)s,      /* tp_str */
2138    (getattrofunc)%(getattr_func)s,                         /* tp_getattro */
2139    0,                         /* tp_setattro */
2140    0,                         /* tp_as_buffer */
2141    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
2142    "%(docstring)s",     /* tp_doc */
2143    0,                         /* tp_traverse */
2144    0,                         /* tp_clear */
2145    0,                         /* tp_richcompare */
2146    0,                         /* tp_weaklistoffset */
2147    (getiterfunc)%(iterator)s,              /* tp_iter */
2148    (iternextfunc)%(iternext)s,/* tp_iternext */
2149    %(class)s_methods,         /* tp_methods */
2150    0,                         /* tp_members */
2151    0,                         /* tp_getset */
2152    0,                         /* tp_base */
2153    0,                         /* tp_dict */
2154    0,                         /* tp_descr_get */
2155    0,                         /* tp_descr_set */
2156    0,                         /* tp_dictoffset */
2157    (initproc)py%(class)s_init,      /* tp_init */
2158    0,                         /* tp_alloc */
2159    0,                         /* tp_new */
2160};
2161""" % args )
2162
2163class StructGenerator(ClassGenerator):
2164    """ A wrapper generator for structs """
2165    def __init__(self, class_name, module):
2166        self.class_name = class_name
2167        self.methods = []
2168        self.module = module
2169        self.base_class_name = None
2170        self.active = False
2171        self.modifier = set()
2172        self.constructor = None
2173        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
2174
2175    def prepare(self):
2176        ## This is needed for late stage initialization - sometimes
2177        ## our class_name is not know until now.
2178        if not self.constructor:
2179            self.constructor = StructConstructor(self.class_name, self.base_class_name,
2180                                                 'Con', [], "void", myclass = self)
2181
2182            self.attributes.rename_class_name(self.class_name)
2183            for x in self.attributes._attributes:
2184                x[1].attributes.add('FOREIGN')
2185
2186    def __str__(self):
2187        result = "#%s\n" % self.docstring
2188
2189        result += "Struct %s:\n" % (self.class_name)
2190        result += "%s\n" % self.attributes
2191
2192        return result
2193
2194    def struct(self, out):
2195        out.write("""\ntypedef struct {
2196  PyObject_HEAD
2197  %(class_name)s *base;
2198  void *ctx;
2199} py%(class_name)s;\n
2200""" % dict(class_name=self.class_name))
2201
2202    def initialise(self):
2203        return ''
2204
2205class ProxyClassGenerator(ClassGenerator):
2206    def __init__(self, class_name, base_class_name, module, *args, **kwargs):
2207        ClassGenerator.__init__(self, class_name, base_class_name, module, *args, **kwargs)
2208        self.constructor = ProxyConstructor(self.class_name,
2209                                            self.base_class_name, '__init__',
2210                                            [('PyObject *', 'proxied')],
2211                                            'void', myclass = self)
2212        self.module = module
2213        self.attributes = ProxiedGetattr(self.class_name, self.base_class_name, self)
2214
2215
2216    def initialise(self):
2217        return "INIT_CLASS(%(class_name)s);\n" % self.__dict__ + ClassGenerator.initialise(self)
2218
2219    def struct(self, out):
2220        out.write("""
2221// The proxied type is an extension of the wrapped type with a pointer
2222// to the proxied PyObject.
2223CLASS(%(class_name)s, %(base_class_name)s)
2224   uint32_t magic;
2225   PyObject *proxied;
2226END_CLASS
2227
2228VIRTUAL(%(class_name)s, %(base_class_name)s) {
2229} END_VIRTUAL
2230
2231typedef struct {
2232  PyObject_HEAD
2233  %(class_name)s base;
2234  void *ctx;
2235} py%(class_name)s;\n
2236""" % self.__dict__)
2237
2238    def PyMethodDef(self, out):
2239        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
2240        ## For now no methods
2241        out.write("     {NULL}  /* Sentinel */\n};\n")
2242
2243class parser:
2244    class_re = re.compile(r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)")
2245    method_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?")
2246    enum_start_re = re.compile(r'enum ([0-9A-Z_a-z]+) {')
2247    enum_re = re.compile(r'([0-9A-Z_a-z]+) = [^,]+,')
2248    enum_end_re = re.compile('}')
2249    arg_re = re.compile(r"\s*([0-9A-Z a-z_]+\s+\*?)([0-9A-Za-z_]+),?")
2250    constant_re = re.compile(r"#define\s+([A-Z_0-9]+)\s+[^\s]+")
2251    struct_re = re.compile(r"([A-Z]+)?\s+(typedef )?struct\s+([A-Z_a-z0-9]+)\s+{")
2252    proxy_class_re = re.compile(r"^([A-Z]+)?\s*PROXY_CLASS\(([A-Za-z0-9]+)\)")
2253    end_class_re = re.compile("END_CLASS")
2254    attribute_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z]+)\s*;")
2255    comment_re = re.compile(r"^\s*//")
2256    comment_start_re = re.compile(r"/\*+")
2257    comment_end_re = re.compile(r"\*+/")
2258    blank_line_re = re.compile("\s+")
2259    typedef_re = re.compile("typedef ([A-Za-z_0-9]+) +([A-Za-z_0-9]+) *;")
2260    bind_re = re.compile("BIND\(([a-zA-Z_0-9]+)\);")
2261    current_class = None
2262
2263    def __init__(self, module, verbosity=0):
2264        self.module = module
2265        self.current_comment = ''
2266        self.verbosity = verbosity
2267        self.state = 'DEFAULT'
2268
2269        ## this is a list of objects which are to be bound
2270        self.to_bind = []
2271
2272        global DEBUG
2273
2274        DEBUG = verbosity
2275
2276        io = StringIO.StringIO("""
2277// Base object
2278CLASS(Object, Obj)
2279END_CLASS
2280""")
2281        self.parse_fd(io, '')
2282
2283    def add_class(self, class_name, base_class_name, class_type, handler, docstring, modifier):
2284        try:
2285            self.current_class = self.module.classes[base_class_name].clone(class_name)
2286        except (KeyError, AttributeError):
2287            log("Base class %s is not defined !!!!" % base_class_name)
2288            self.current_class = class_type(class_name, base_class_name, self.module)
2289
2290        ## Now add the new class to the module object
2291        self.current_class.docstring = docstring
2292        self.current_class.modifier = modifier
2293        self.module.add_class(self.current_class, handler)
2294
2295    def parse_filenames(self, filenames):
2296        ## Be quiet for the first pass as many problems will be
2297        ## resolved on the second pass anyway.
2298        global DEBUG
2299        DEBUG = 0
2300        for f in filenames:
2301            self._parse(f)
2302
2303        DEBUG = self.verbosity
2304        log("Second pass: Consolidating definitions")
2305        for f in filenames:
2306            self._parse(f)
2307
2308    def _parse(self, filename):
2309        fd = open(filename)
2310        self.parse_fd(fd, filename)
2311        fd.close()
2312
2313        if filename not in self.module.files:
2314              self.module.headers += '#include "%s"\n' % filename
2315              self.module.files.append(filename)
2316
2317    def parse_fd(self, fd, filename):
2318        self.current_class = None
2319        self.current_comment = ''
2320
2321        while 1:
2322            line = fd.readline()
2323            if not line: break
2324
2325            ## Handle binds
2326            m= self.bind_re.search(line)
2327            if m:
2328                print "Will bind %s" % m.group(1)
2329                self.to_bind.append(m.group(1))
2330                continue
2331
2332            ## Handle enums
2333            if self.state == 'DEFAULT':
2334                m = self.enum_start_re.search(line)
2335                if m:
2336                    self.state = 'enum'
2337                    type_dispatcher[m.group(1)] = type_dispatcher['int']
2338                    continue
2339
2340            elif self.state == 'enum':
2341                m = self.enum_re.search(line)
2342                if m:
2343                    self.module.add_constant(m.group(1), 'integer')
2344
2345                if '}' in line:
2346                    self.state = 'DEFAULT'
2347                    continue
2348
2349            ## Handle c++ style comments //
2350            m = self.comment_re.match(line)
2351            if m:
2352                self.current_comment = line[m.end():]
2353                while 1:
2354                    line = fd.readline()
2355
2356                    m = self.comment_re.match(line)
2357                    if not m:
2358                        break
2359
2360                    self.current_comment += line[m.end():]
2361
2362            ## Multiline C style comments
2363            m = self.comment_start_re.search(line)
2364            if m:
2365                line = line[m.end():]
2366                while 1:
2367                    m = self.comment_end_re.search(line)
2368                    if m:
2369                        self.current_comment += line[:m.start()]
2370                        line = fd.readline()
2371                        break
2372                    else:
2373                        self.current_comment += line
2374
2375                    line = fd.readline()
2376                    if not line: break
2377
2378            ## Handle simple typdefs (as if typedef uint64_t my_fancy_type;)
2379            m = self.typedef_re.search(line)
2380            if m:
2381                ## We basically add a new type as a copy of the old
2382                ## type
2383                old, new = m.group(1), m.group(2)
2384                if old in type_dispatcher:
2385                    type_dispatcher[new] = type_dispatcher[old]
2386
2387            ## Handle constant #define
2388            m = self.constant_re.search(line)
2389            if m:
2390                ## We need to determine if it is a string or integer
2391                if re.search('"', line):
2392                    ## Its a string
2393                    self.module.add_constant(m.group(1), 'string')
2394                else:
2395                    self.module.add_constant(m.group(1), 'integer')
2396
2397            ## Wrap structs
2398            m = self.struct_re.search(line)
2399            if m:
2400                modifier = m.group(1)
2401                class_name = m.group(3)
2402                base_class_name = None
2403                ## Structs may be refered to as a pointer or absolute
2404                ## - its the same thing ultimatley.
2405
2406                ## We only wrap structures which are explicitely bound
2407                if (modifier and 'BOUND' in modifier) or \
2408                        class_name in self.to_bind:
2409                    self.add_class(class_name, base_class_name, StructGenerator, StructWrapper,
2410                                   self.current_comment, modifier)
2411                    type_dispatcher["%s *" % class_name] = PointerStructWrapper
2412
2413                continue
2414
2415            m = self.class_re.search(line)
2416            if m:
2417                ## We need to make a new class now... We basically
2418                ## need to build on top of previously declared base
2419                ## classes - so we try to find base classes, clone
2420                ## them if possible:
2421                modifier = m.group(1)
2422                class_name = m.group(2)
2423                base_class_name = m.group(3)
2424                self.add_class(class_name, base_class_name, ClassGenerator, Wrapper,
2425                               self.current_comment, modifier)
2426                type_dispatcher["%s *" % class_name] = PointerWrapper
2427
2428                continue
2429
2430            ## Make a proxy class for python callbacks
2431            m = self.proxy_class_re.search(line)
2432            if m:
2433                modifier = m.group(1)
2434                base_class_name = m.group(2)
2435                class_name = "Proxied%s" % base_class_name
2436                try:
2437                    proxied_class = self.module.classes[base_class_name]
2438                except KeyError:
2439                    raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
2440                self.current_class = ProxyClassGenerator(class_name,
2441                                                         base_class_name, self.module)
2442                #self.current_class.constructor.args += proxied_class.constructor.args
2443                self.current_class.docstring = self.current_comment
2444
2445                ## Create proxies for all these methods
2446                for method in proxied_class.methods:
2447                    self.current_class.methods.append(ProxiedMethod(method, self.current_class))
2448
2449                self.module.add_class(self.current_class, Wrapper)
2450
2451                ## Make sure that further lines are not interpreted as part of this class.
2452                self.current_class = None
2453
2454            m = self.method_re.search(line)
2455            if self.current_class and m:
2456                args = []
2457                method_name = m.group(3)
2458                return_type = m.group(1).strip()
2459                ## Ignore private methods
2460                if return_type.startswith("PRIVATE"): continue
2461
2462                ## Now parse the args
2463                offset = m.end()
2464                while 1:
2465                    m = self.arg_re.match(line[offset:])
2466                    if not m:
2467                        ## Allow multiline definitions if there is \\
2468                        ## at the end of the line
2469                        if line.strip().endswith("\\"):
2470                            line = fd.readline()
2471                            offset = 0
2472                            if line:
2473                                continue
2474
2475                        break
2476
2477                    offset += m.end()
2478                    args.append([m.group(1).strip(), m.group(2).strip()])
2479
2480                if return_type == self.current_class.class_name and \
2481                        method_name.startswith("Con"):
2482                    self.current_class.add_constructor(method_name, args, return_type,
2483                                                       self.current_comment)
2484                else:
2485                    self.current_class.add_method(method_name, args, return_type,
2486                                                  self.current_comment)
2487
2488            m = self.attribute_re.search(line)
2489            if self.current_class and m:
2490                type = m.group(1)
2491                name = m.group(2)
2492                self.current_class.add_attribute(name, type)
2493
2494            m = self.end_class_re.search(line)
2495            if m:
2496                ## Just clear the current class context
2497                self.current_class = None
2498
2499            ## If this is a shadow file we do not include it from the
2500            ## main module. A shadow file is a way for us to redefine
2501            ## other C constructs which will be imported from an
2502            ## external header file so we can bind them slightly
2503            ## differently.
2504            if "this is a shadow file" in self.current_comment \
2505                    and filename not in self.module.files:
2506                self.module.files.append(filename)
2507
2508            ## We only care about comment immediately above methods
2509            ## etc as we take them to be documentation. If we get here
2510            ## the comment is not above anything we care about - so we
2511            ## clear it:
2512            self.current_comment = ''
2513
2514    def write(self, out):
2515        self.module.write(out)
2516
2517
2518import lexer
2519
2520class EnumConstructor(ConstructorMethod):
2521    def write_destructor(self, out):
2522        out.write("""static void
2523%(class_name)s_dealloc(py%(class_name)s *self) {
2524 Py_DECREF(self->value);
2525 PyObject_Del(self);
2526};\n
2527""" % dict(class_name = self.class_name))
2528    def write_definition(self, out):
2529        self.myclass.modifier.add("TP_STR")
2530        self._prototype(out)
2531        out.write("""{
2532static char *kwlist[] = {"value", NULL};
2533
2534if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &self->value))
2535 goto error;
2536
2537Py_INCREF(self->value);
2538
2539  return 0;
2540error:
2541    return -1;
2542};
2543
2544static PyObject *py%(class_name)s___str__(py%(class_name)s *self) {
2545  PyObject *result = PyDict_GetItem(%(class_name)s_rev_lookup, self->value);
2546
2547  if(result) {
2548     Py_INCREF(result);
2549 } else {
2550     result = PyObject_Str(self->value);
2551 };
2552
2553 return result;
2554};
2555
2556""" % self.__dict__)
2557
2558class Enum(StructGenerator):
2559    def __init__(self, name, module):
2560        StructGenerator.__init__(self, name, module)
2561        self.values = []
2562        self.name = name
2563        self.attributes = None
2564        self.active = True
2565
2566    def prepare(self):
2567        self.constructor = EnumConstructor(self.class_name, self.base_class_name,
2568                                           'Con', [], "void", myclass = self)
2569        StructGenerator.prepare(self)
2570
2571    def __str__(self):
2572        result = "Enum %s:\n" % (self.name)
2573        for attr in self.values:
2574            result += "    %s\n" % attr.__str__()
2575
2576        return result
2577
2578    def struct(self,out):
2579        out.write("""\ntypedef struct {
2580  PyObject_HEAD
2581  PyObject *value;
2582} py%(class_name)s;\n
2583
2584
2585static PyObject *%(class_name)s_Dict_lookup;
2586static PyObject *%(class_name)s_rev_lookup;
2587""" % dict(class_name=self.class_name))
2588
2589    def PyMethodDef(self, out):
2590        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
2591        out.write("     {NULL}  /* Sentinel */\n};\n")
2592
2593    def numeric_protocol_nonzero(self):
2594        pass
2595
2596    def numeric_protocol_int(self):
2597        return """
2598static PyObject *%(class_name)s_int(py%(class_name)s *self) {
2599    Py_INCREF(self->value);
2600    return self->value;
2601};
2602""" % self.__dict__
2603
2604    def initialise(self):
2605        result = """
2606%(class_name)s_Dict_lookup = PyDict_New();
2607%(class_name)s_rev_lookup = PyDict_New();
2608""" % self.__dict__
2609
2610        if self.values:
2611            result += "{ PyObject *tmp, *tmp2;\n"
2612            for attr in self.values:
2613                result += ''' tmp = PyLong_FromLong(%(value)s);
2614  tmp2 = PyString_FromString("%(value)s");
2615  PyDict_SetItem(%(class_name)s_Dict_lookup, tmp2, tmp);
2616  PyDict_SetItem(%(class_name)s_rev_lookup, tmp, tmp2);
2617  Py_DECREF(tmp);
2618  Py_DECREF(tmp2);
2619
2620''' % dict(value = attr, class_name=self.class_name)
2621            result += "};\n"
2622
2623        return result
2624
2625class EnumType(Integer):
2626    buildstr = 'i'
2627
2628    def __init__(self, name, type):
2629        Integer.__init__(self, name, type)
2630        self.type = type
2631
2632    def to_python_object(self, name=None, result='py_result', **kw):
2633        name = name or self.name
2634        return """PyErr_Clear();
2635%s = PyObject_CallMethod(g_module, "%s", "K", (uint64_t)%s);
2636""" % (result, self.type, name)
2637
2638    def pre_call(self, method):
2639        method.error_set = True
2640        return """
2641// Check if the integer passed is actually a valid member of the enum
2642// Enum value of 0 is always allowed
2643if(%(name)s) { PyObject *py_%(name)s = PyLong_FromLong(%(name)s);
2644  PyObject *tmp = PyDict_GetItem(%(type)s_rev_lookup, py_%(name)s);
2645
2646  Py_DECREF(py_%(name)s);
2647  if(!tmp) {
2648    PyErr_Format(PyExc_RuntimeError, "value %%lu is not valid for Enum %(type)s of arg '%(name)s'", (unsigned long)%(name)s);
2649    goto error;
2650  };
2651  Py_DECREF(tmp);
2652};
2653""" % self.__dict__
2654
2655class HeaderParser(lexer.SelfFeederMixIn):
2656    tokens = [
2657        [ 'INITIAL', r'#define\s+', 'PUSH_STATE', 'DEFINE' ],
2658        [ 'DEFINE', r'([A-Za-z_0-9]+)\s+[^\n]+', 'DEFINE,POP_STATE', None ],
2659        [ 'DEFINE', r'\n', 'POP_STATE', None],
2660
2661        ## Recognize ansi c comments
2662        [ '.', r'/\*(.)', 'PUSH_STATE', 'COMMENT' ],
2663        [ 'COMMENT', r'(.+?)\*/\s+', 'COMMENT_END,POP_STATE', None],
2664        [ 'COMMENT', r'(.+)', 'COMMENT', None],
2665
2666        ## And c++ comments
2667        [ '.', r'//([^\n]+)', 'COMMENT', None],
2668
2669        ## An empty line clears the current comment
2670        [ '.', r'\r?\n\r?\n', 'CLEAR_COMMENT', None],
2671
2672        ## Ignore whitespace
2673        [ '.', r'\s+', 'SPACE', None ],
2674        [ '.', r'\\\n', 'SPACE', None ],
2675
2676        ## Recognize CLASS() definitions
2677        [ 'INITIAL', r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)",
2678                     'PUSH_STATE,CLASS_START', 'CLASS'],
2679
2680        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT|PRIVATE)?([0-9A-Z_a-z ]+( |\*))METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?",
2681                     "PUSH_STATE,METHOD_START", "METHOD"],
2682        [ 'METHOD', r"\s*([0-9A-Z a-z_]+\s+\*?\*?)([0-9A-Za-z_]+),?", "METHOD_ARG", None ],
2683        [ 'METHOD', r'\);', 'POP_STATE,METHOD_END', None],
2684
2685        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT)?([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
2686                   'CLASS_ATTRIBUTE', None],
2687        [ 'CLASS', "END_CLASS", 'END_CLASS,POP_STATE', None],
2688
2689        ## Recognize struct definitions (With name)
2690        [ 'INITIAL', "([A-Z_a-z0-9 ]+)?struct\s+([A-Z_a-z0-9]+)\s+{",
2691                     'PUSH_STATE,STRUCT_START', 'STRUCT'],
2692
2693        ## Without name (using typedef)
2694        [ 'INITIAL', "typedef\s+struct\s+{",
2695                     'PUSH_STATE,TYPEDEF_STRUCT_START', 'STRUCT'],
2696
2697        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
2698                     'STRUCT_ATTRIBUTE', None],
2699
2700        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+)\*\s+([A-Z_a-z0-9]+)\s*;",
2701                     'STRUCT_ATTRIBUTE_PTR', None],
2702
2703        ## Struct ended with typedef
2704        [ 'STRUCT', '}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEF_STRUCT_END', None],
2705        [ 'STRUCT', '}', 'POP_STATE,STRUCT_END', None],
2706
2707        ## Handle recursive struct or union definition (At the moment
2708        ## we cant handle them at all)
2709        [ '(RECURSIVE_)?STRUCT', '(struct|union)\s+([_A-Za-z0-9]+)?\s*{', 'PUSH_STATE', 'RECURSIVE_STRUCT'],
2710        [ 'RECURSIVE_STRUCT', '}\s+[0-9A-Za-z]+', 'POP_STATE', None],
2711
2712        ## Process enums (2 forms - named and typedefed)
2713        [ 'INITIAL', r'enum\s+([0-9A-Za-z_]+)\s+{', 'PUSH_STATE,ENUM_START', 'ENUM' ],
2714        ## Unnamed
2715        [ 'INITIAL', r'typedef\s+enum\s+{', 'PUSH_STATE,TYPEDEF_ENUM_START', 'ENUM' ],
2716        [ 'ENUM', r'([0-9A-Za-z_]+)\s+=[^\n]+', 'ENUM_VALUE', None],
2717
2718        ## Typedefed ending
2719        [ 'ENUM', r'}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEFED_ENUM_END', None],
2720        [ 'ENUM', r'}', 'POP_STATE,ENUM_END', None],
2721
2722        [ 'INITIAL', r'BIND_STRUCT\(([0-9A-Za-z_ \*]+)\)', 'BIND_STRUCT', None],
2723
2724        ## A simple typedef of one type for another type:
2725        [ 'INITIAL', r"typedef ([A-Za-z_0-9]+) +([^;]+);", 'SIMPLE_TYPEDEF', None],
2726
2727        ## Handle proxied directives
2728        [ 'INITIAL', r"PROXY_CLASS\(([A-Za-z0-9]+)\);", 'PROXY_CLASS', None],
2729
2730        ]
2731
2732    def __init__(self, name, verbose = 1):
2733        self.module = Module(name)
2734        lexer.SelfFeederMixIn.__init__(self, verbose = 0)
2735
2736        io = StringIO.StringIO("""
2737// Base object
2738CLASS(Object, Obj)
2739END_CLASS
2740""")
2741        self.parse_fd(io)
2742
2743    current_comment = ''
2744    def COMMENT(self, t, m):
2745        self.current_comment += m.group(1) + "\n"
2746
2747    def COMMENT_END(self, t, m):
2748        self.current_comment += m.group(1)
2749
2750    def CLEAR_COMMENT(self, t, m):
2751        self.current_comment = ''
2752
2753    def DEFINE(self, t, m):
2754        line = m.group(0)
2755        line = line.split('/*')[0]
2756        if '"' in line:
2757            type = 'string'
2758        else:
2759            type = 'integer'
2760
2761        name = m.group(1).strip()
2762        if len(name)>3 and name[0]!='_' and name==name.upper():
2763            self.module.add_constant(name, type)
2764
2765    current_class = None
2766    def CLASS_START(self, t, m):
2767        class_name = m.group(2).strip()
2768        base_class_name = m.group(3).strip()
2769
2770        try:
2771            self.current_class = self.module.classes[base_class_name].clone(class_name)
2772        except (KeyError, AttributeError):
2773            log("Base class %s is not defined !!!!" % base_class_name)
2774            self.current_class = ClassGenerator(class_name, base_class_name, self.module)
2775
2776        self.current_class.docstring = self.current_comment
2777        self.current_class.modifier.add(m.group(1))
2778        self.module.add_class(self.current_class, Wrapper)
2779        type_dispatcher["%s *" % class_name] = PointerWrapper
2780
2781    current_method = None
2782    def METHOD_START(self, t, m):
2783        return_type = m.group(2).strip()
2784        method_name = m.group(5).strip()
2785        modifier = m.group(1) or ''
2786
2787        if 'PRIVATE' in modifier: return
2788
2789        ## Is it a regular method or a constructor?
2790        self.current_method = Method
2791        if return_type == self.current_class.class_name and \
2792                method_name.startswith("Con"):
2793            self.current_method = ConstructorMethod
2794        elif method_name == 'iternext':
2795            self.current_method = IteratorMethod
2796            self.current_class.modifier.add("ITERATOR")
2797        elif method_name == '__iter__':
2798            self.current_method = SelfIteratorMethod
2799            self.current_class.modifier.add("SELF_ITER")
2800        elif method_name == '__str__':
2801            self.current_class.modifier.add("TP_STR")
2802
2803        self.current_method = self.current_method(self.current_class.class_name,
2804                                                  self.current_class.base_class_name,
2805                                                  method_name, [], return_type,
2806                                                  myclass = self.current_class)
2807        self.current_method.docstring = self.current_comment
2808        self.current_method.modifier = modifier
2809
2810    def METHOD_ARG(self, t, m):
2811        name = m.group(2).strip()
2812        type = m.group(1).strip()
2813        if self.current_method:
2814            self.current_method.add_arg(type, name)
2815
2816    def METHOD_END(self, t, m):
2817        if not self.current_method: return
2818
2819        if isinstance(self.current_method, ConstructorMethod):
2820            self.current_class.constructor = self.current_method
2821        else:
2822            found = False
2823            for i in range(len(self.current_class.methods)):
2824                ## Try to replace existing methods with this new method
2825                method = self.current_class.methods[i]
2826                if method.name == self.current_method.name:
2827                    self.current_class.methods[i] = self.current_method
2828                    self.current_method = None
2829                    return
2830
2831            ## Method does not exist, just add to the end
2832            self.current_class.methods.append(self.current_method)
2833
2834        self.current_method = None
2835
2836    def CLASS_ATTRIBUTE(self, t, m):
2837        modifier = m.group(1) or ''
2838        type = m.group(2).strip()
2839        name = m.group(3).strip()
2840        self.current_class.add_attribute(name, type, modifier)
2841
2842    def END_CLASS(self, t, m):
2843        self.current_class = None
2844
2845    current_struct = None
2846    def STRUCT_START(self, t, m):
2847        self.current_struct = StructGenerator(m.group(2).strip(), self.module)
2848        self.current_struct.docstring = self.current_comment
2849        self.current_struct.modifier.add(m.group(1))
2850
2851    def TYPEDEF_STRUCT_START(self, t, m):
2852        self.current_struct = StructGenerator(None, self.module)
2853        self.current_struct.docstring = self.current_comment
2854
2855    def STRUCT_ATTRIBUTE(self, t, m):
2856        name = m.group(2).strip()
2857        type = m.group(1).strip()
2858        self.current_struct.add_attribute(name, type, '')
2859
2860    def STRUCT_ATTRIBUTE_PTR(self, t, m):
2861        type = "%s *" % m.group(1).strip()
2862        name = m.group(2).strip()
2863        self.current_struct.add_attribute(name, type, '')
2864
2865    def STRUCT_END(self, t, m):
2866        self.module.add_class(self.current_struct, StructWrapper)
2867        type_dispatcher["%s *" % self.current_struct.class_name] = PointerStructWrapper
2868        self.current_struct = None
2869
2870    def TYPEDEF_STRUCT_END(self, t, m):
2871        self.current_struct.class_name = m.group(1).strip()
2872
2873        self.STRUCT_END(t, m)
2874
2875    current_enum = None
2876    def ENUM_START(self, t, m):
2877        self.current_enum = Enum(m.group(1).strip(), self.module)
2878
2879    def TYPEDEF_ENUM_START(self, t, m):
2880        self.current_enum = Enum(None, self.module)
2881
2882    def ENUM_VALUE(self, t, m):
2883        self.current_enum.values.append(m.group(1).strip())
2884
2885    def ENUM_END(self, t, m):
2886        self.module.classes[self.current_enum.name] = self.current_enum
2887
2888        ## For now we just treat enums as an integer, and also add
2889        ## them to the constant table. In future it would be nice to
2890        ## have them as a proper python object so we can override
2891        ## __str__ and __int__.
2892        for attr in self.current_enum.values:
2893            self.module.add_constant(attr, 'integer')
2894
2895        #type_dispatcher[self.current_enum.name] = Integer
2896        type_dispatcher[self.current_enum.name] = EnumType
2897        self.current_enum = None
2898
2899    def TYPEDEFED_ENUM_END(self, t, m):
2900        self.current_enum.name = self.current_enum.class_name = m.group(1)
2901        self.ENUM_END(t, m)
2902
2903    def BIND_STRUCT(self, t, m):
2904        self.module.classes[m.group(1)].active = True
2905
2906    def SIMPLE_TYPEDEF(self, t, m):
2907        ## We basically add a new type as a copy of the old
2908        ## type
2909        old, new = m.group(1).strip(), m.group(2).strip()
2910        if old in type_dispatcher:
2911            type_dispatcher[new] = type_dispatcher[old]
2912
2913    def PROXY_CLASS(self, t, m):
2914        base_class_name = m.group(1).strip()
2915        class_name = "Proxied%s" % base_class_name
2916        try:
2917            proxied_class = self.module.classes[base_class_name]
2918        except KeyError:
2919            raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
2920        current_class = ProxyClassGenerator(class_name,
2921                                            base_class_name, self.module)
2922        #self.current_class.constructor.args += proxied_class.constructor.args
2923        current_class.docstring = self.current_comment
2924
2925        ## Create proxies for all these methods
2926        for method in proxied_class.methods:
2927            current_class.methods.append(ProxiedMethod(method, current_class))
2928
2929        self.module.add_class(current_class, Wrapper)
2930
2931    def parse_filenames(self, filenames):
2932        for f in filenames:
2933            self._parse(f)
2934
2935        ## Second pass
2936        for f in filenames:
2937            self._parse(f)
2938
2939    def _parse(self, filename):
2940        fd = open(filename)
2941        self.parse_fd(fd)
2942        fd.close()
2943
2944        if filename not in self.module.files:
2945              self.module.headers += '#include "%s"\n' % filename
2946              self.module.files.append(filename)
2947
2948    def write(self, out):
2949        self.module.write(out)
2950
2951
2952if __name__ == '__main__':
2953    p = HeaderParser('pyaff4', verbose = 1)
2954    for arg in sys.argv[1:]:
2955        p.parse_fd(open(arg))
2956
2957    log("second parse")
2958    for arg in sys.argv[1:]:
2959        p.parse_fd(open(arg))
2960
2961    pdb.set_trace()
2962    p.write(sys.stdout)
2963
2964#    p = parser(Module("pyaff4"))
2965#    for arg in sys.argv[1:]:
2966#        p.parse(arg)
2967#        log("second parse")
2968#        p.parse(arg)
2969
2970#    p.write(sys.stdout)
2971
Note: See TracBrowser for help on using the repository browser.