source: trunk/python/experimental/class_parser.py @ 273

Last change on this file since 273 was 198, checked in by tim, 15 years ago

misc tweaks to run under python2.5-dbg

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