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

Last change on this file since 243 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.