1from bgenOutput import *
2from bgenGeneratorGroup import GeneratorGroup
3
4class ObjectDefinition(GeneratorGroup):
5    "Spit out code that together defines a new Python object type"
6    basechain = "NULL"
7    tp_flags = "Py_TPFLAGS_DEFAULT"
8    basetype = None
9    argref = ""    # set to "*" if arg to <type>_New should be pointer
10    argconst = ""   # set to "const " if arg to <type>_New should be const
11
12    def __init__(self, name, prefix, itselftype):
13        """ObjectDefinition constructor.  May be extended, but do not override.
14
15        - name: the object's official name, e.g. 'SndChannel'.
16        - prefix: the prefix used for the object's functions and data, e.g. 'SndCh'.
17        - itselftype: the C type actually contained in the object, e.g. 'SndChannelPtr'.
18
19        XXX For official Python data types, rules for the 'Py' prefix are a problem.
20        """
21
22        GeneratorGroup.__init__(self, prefix or name)
23        self.name = name
24        self.itselftype = itselftype
25        self.objecttype = name + 'Object'
26        self.typename = name + '_Type'
27        self.static = "static " # set to "" to make <type>_New and <type>_Convert public
28        self.modulename = None
29        if hasattr(self, "assertions"):
30            self.assertions()
31
32    def add(self, g, dupcheck=0):
33        g.setselftype(self.objecttype, self.itselftype)
34        GeneratorGroup.add(self, g, dupcheck)
35
36    def reference(self):
37        # In case we are referenced from a module
38        pass
39
40    def setmodulename(self, name):
41        self.modulename = name
42
43    def generate(self):
44        # XXX This should use long strings and %(varname)s substitution!
45
46        OutHeader2("Object type " + self.name)
47
48        self.outputCheck()
49
50        Output("typedef struct %s {", self.objecttype)
51        IndentLevel()
52        Output("PyObject_HEAD")
53        self.outputStructMembers()
54        DedentLevel()
55        Output("} %s;", self.objecttype)
56
57        self.outputNew()
58
59        self.outputConvert()
60
61        self.outputDealloc()
62
63        GeneratorGroup.generate(self)
64
65        Output()
66        self.outputMethodChain()
67
68        self.outputGetattr()
69
70        self.outputSetattr()
71
72        self.outputCompare()
73
74        self.outputRepr()
75
76        self.outputHash()
77
78        self.outputPEP253Hooks()
79
80        self.outputTypeObject()
81
82        OutHeader2("End object type " + self.name)
83
84    def outputCheck(self):
85        sf = self.static and "static "
86        Output("%sPyTypeObject %s;", sf, self.typename)
87        Output()
88        Output("#define %s_Check(x) ((x)->ob_type == &%s || PyObject_TypeCheck((x), &%s))",
89               self.prefix, self.typename, self.typename)
90        Output()
91
92    def outputMethodChain(self):
93        Output("%sPyMethodChain %s_chain = { %s_methods, %s };",
94                self.static,    self.prefix, self.prefix, self.basechain)
95
96    def outputStructMembers(self):
97        Output("%s ob_itself;", self.itselftype)
98
99    def outputNew(self):
100        Output()
101        Output("%sPyObject *%s_New(%s%s %sitself)", self.static, self.prefix,
102                self.argconst, self.itselftype, self.argref)
103        OutLbrace()
104        Output("%s *it;", self.objecttype)
105        self.outputCheckNewArg()
106        Output("it = PyObject_NEW(%s, &%s);", self.objecttype, self.typename)
107        Output("if (it == NULL) return NULL;")
108        if self.basetype:
109            Output("/* XXXX Should we tp_init or tp_new our basetype? */")
110        self.outputInitStructMembers()
111        Output("return (PyObject *)it;")
112        OutRbrace()
113
114    def outputInitStructMembers(self):
115        Output("it->ob_itself = %sitself;", self.argref)
116
117    def outputCheckNewArg(self):
118        "Override this method to apply additional checks/conversions"
119
120    def outputConvert(self):
121        Output()
122        Output("%sint %s_Convert(PyObject *v, %s *p_itself)", self.static, self.prefix,
123                self.itselftype)
124        OutLbrace()
125        self.outputCheckConvertArg()
126        Output("if (!%s_Check(v))", self.prefix)
127        OutLbrace()
128        Output('PyErr_SetString(PyExc_TypeError, "%s required");', self.name)
129        Output("return 0;")
130        OutRbrace()
131        Output("*p_itself = ((%s *)v)->ob_itself;", self.objecttype)
132        Output("return 1;")
133        OutRbrace()
134
135    def outputCheckConvertArg(self):
136        "Override this method to apply additional conversions"
137
138    def outputDealloc(self):
139        Output()
140        Output("static void %s_dealloc(%s *self)", self.prefix, self.objecttype)
141        OutLbrace()
142        self.outputCleanupStructMembers()
143        if self.basetype:
144            Output("%s.tp_dealloc((PyObject *)self);", self.basetype)
145        elif hasattr(self, 'output_tp_free'):
146            # This is a new-style object with tp_free slot
147            Output("self->ob_type->tp_free((PyObject *)self);")
148        else:
149            Output("PyObject_Free((PyObject *)self);")
150        OutRbrace()
151
152    def outputCleanupStructMembers(self):
153        self.outputFreeIt("self->ob_itself")
154
155    def outputFreeIt(self, name):
156        Output("/* Cleanup of %s goes here */", name)
157
158    def outputGetattr(self):
159        Output()
160        Output("static PyObject *%s_getattr(%s *self, char *name)", self.prefix, self.objecttype)
161        OutLbrace()
162        self.outputGetattrBody()
163        OutRbrace()
164
165    def outputGetattrBody(self):
166        self.outputGetattrHook()
167        Output("return Py_FindMethodInChain(&%s_chain, (PyObject *)self, name);",
168               self.prefix)
169
170    def outputGetattrHook(self):
171        pass
172
173    def outputSetattr(self):
174        Output()
175        Output("#define %s_setattr NULL", self.prefix)
176
177    def outputCompare(self):
178        Output()
179        Output("#define %s_compare NULL", self.prefix)
180
181    def outputRepr(self):
182        Output()
183        Output("#define %s_repr NULL", self.prefix)
184
185    def outputHash(self):
186        Output()
187        Output("#define %s_hash NULL", self.prefix)
188
189    def outputTypeObject(self):
190        sf = self.static and "static "
191        Output()
192        Output("%sPyTypeObject %s = {", sf, self.typename)
193        IndentLevel()
194        Output("PyObject_HEAD_INIT(NULL)")
195        Output("0, /*ob_size*/")
196        if self.modulename:
197            Output("\"%s.%s\", /*tp_name*/", self.modulename, self.name)
198        else:
199            Output("\"%s\", /*tp_name*/", self.name)
200        Output("sizeof(%s), /*tp_basicsize*/", self.objecttype)
201        Output("0, /*tp_itemsize*/")
202        Output("/* methods */")
203        Output("(destructor) %s_dealloc, /*tp_dealloc*/", self.prefix)
204        Output("0, /*tp_print*/")
205        Output("(getattrfunc) %s_getattr, /*tp_getattr*/", self.prefix)
206        Output("(setattrfunc) %s_setattr, /*tp_setattr*/", self.prefix)
207        Output("(cmpfunc) %s_compare, /*tp_compare*/", self.prefix)
208        Output("(reprfunc) %s_repr, /*tp_repr*/", self.prefix)
209        Output("(PyNumberMethods *)0, /* tp_as_number */")
210        Output("(PySequenceMethods *)0, /* tp_as_sequence */")
211        Output("(PyMappingMethods *)0, /* tp_as_mapping */")
212        Output("(hashfunc) %s_hash, /*tp_hash*/", self.prefix)
213        DedentLevel()
214        Output("};")
215
216    def outputTypeObjectInitializer(self):
217        Output("""%s.ob_type = &PyType_Type;""", self.typename)
218        if self.basetype:
219            Output("%s.tp_base = &%s;", self.typename, self.basetype)
220        Output("if (PyType_Ready(&%s) < 0) return;", self.typename)
221        Output("""Py_INCREF(&%s);""", self.typename)
222        Output("PyModule_AddObject(m, \"%s\", (PyObject *)&%s);", self.name, self.typename);
223        self.outputTypeObjectInitializerCompat()
224
225    def outputTypeObjectInitializerCompat(self):
226        Output("/* Backward-compatible name */")
227        Output("""Py_INCREF(&%s);""", self.typename);
228        Output("PyModule_AddObject(m, \"%sType\", (PyObject *)&%s);", self.name, self.typename);
229
230    def outputPEP253Hooks(self):
231        pass
232
233class PEP252Mixin:
234    getsetlist = []
235
236    def assertions(self):
237        # Check that various things aren't overridden. If they are it could
238        # signify a bgen-client that has been partially converted to PEP252.
239        assert self.outputGetattr.im_func == PEP252Mixin.outputGetattr.im_func
240        assert self.outputSetattr.im_func == PEP252Mixin.outputSetattr.im_func
241        assert self.outputGetattrBody == None
242        assert self.outputGetattrHook == None
243        assert self.basechain == "NULL"
244
245    def outputGetattr(self):
246        pass
247
248    outputGetattrBody = None
249
250    outputGetattrHook = None
251
252    def outputSetattr(self):
253        pass
254
255    def outputMethodChain(self):
256        # This is a good place to output the getters and setters
257        self.outputGetSetList()
258
259    def outputHook(self, name):
260        methodname = "outputHook_" + name
261        if hasattr(self, methodname):
262            func = getattr(self, methodname)
263            func()
264        else:
265            Output("0, /*%s*/", name)
266
267    def outputTypeObject(self):
268        sf = self.static and "static "
269        Output()
270        Output("%sPyTypeObject %s = {", sf, self.typename)
271        IndentLevel()
272        Output("PyObject_HEAD_INIT(NULL)")
273        Output("0, /*ob_size*/")
274        if self.modulename:
275            Output("\"%s.%s\", /*tp_name*/", self.modulename, self.name)
276        else:
277            Output("\"%s\", /*tp_name*/", self.name)
278        Output("sizeof(%s), /*tp_basicsize*/", self.objecttype)
279        Output("0, /*tp_itemsize*/")
280
281        Output("/* methods */")
282        Output("(destructor) %s_dealloc, /*tp_dealloc*/", self.prefix)
283        Output("0, /*tp_print*/")
284        Output("(getattrfunc)0, /*tp_getattr*/")
285        Output("(setattrfunc)0, /*tp_setattr*/")
286        Output("(cmpfunc) %s_compare, /*tp_compare*/", self.prefix)
287        Output("(reprfunc) %s_repr, /*tp_repr*/", self.prefix)
288
289        Output("(PyNumberMethods *)0, /* tp_as_number */")
290        Output("(PySequenceMethods *)0, /* tp_as_sequence */")
291        Output("(PyMappingMethods *)0, /* tp_as_mapping */")
292
293        Output("(hashfunc) %s_hash, /*tp_hash*/", self.prefix)
294        self.outputHook("tp_call")
295        Output("0, /*tp_str*/")
296        Output("PyObject_GenericGetAttr, /*tp_getattro*/")
297        Output("PyObject_GenericSetAttr, /*tp_setattro */")
298
299        self.outputHook("tp_as_buffer")
300        Output("%s, /* tp_flags */", self.tp_flags)
301        self.outputHook("tp_doc")
302        self.outputHook("tp_traverse")
303        self.outputHook("tp_clear")
304        self.outputHook("tp_richcompare")
305        self.outputHook("tp_weaklistoffset")
306        self.outputHook("tp_iter")
307        self.outputHook("tp_iternext")
308        Output("%s_methods, /* tp_methods */", self.prefix)
309        self.outputHook("tp_members")
310        Output("%s_getsetlist, /*tp_getset*/", self.prefix)
311        self.outputHook("tp_base")
312        self.outputHook("tp_dict")
313        self.outputHook("tp_descr_get")
314        self.outputHook("tp_descr_set")
315        self.outputHook("tp_dictoffset")
316        self.outputHook("tp_init")
317        self.outputHook("tp_alloc")
318        self.outputHook("tp_new")
319        self.outputHook("tp_free")
320        DedentLevel()
321        Output("};")
322
323    def outputGetSetList(self):
324        if self.getsetlist:
325            for name, get, set, doc in self.getsetlist:
326                if get:
327                    self.outputGetter(name, get)
328                else:
329                    Output("#define %s_get_%s NULL", self.prefix, name)
330                    Output()
331                if set:
332                    self.outputSetter(name, set)
333                else:
334                    Output("#define %s_set_%s NULL", self.prefix, name)
335                    Output()
336
337            Output("static PyGetSetDef %s_getsetlist[] = {", self.prefix)
338            IndentLevel()
339            for name, get, set, doc in self.getsetlist:
340                if doc:
341                    doc = '"' + doc + '"'
342                else:
343                    doc = "NULL"
344                Output("{\"%s\", (getter)%s_get_%s, (setter)%s_set_%s, %s},",
345                    name, self.prefix, name, self.prefix, name, doc)
346            Output("{NULL, NULL, NULL, NULL},")
347            DedentLevel()
348            Output("};")
349        else:
350            Output("#define %s_getsetlist NULL", self.prefix)
351        Output()
352
353    def outputGetter(self, name, code):
354        Output("static PyObject *%s_get_%s(%s *self, void *closure)",
355            self.prefix, name, self.objecttype)
356        OutLbrace()
357        Output(code)
358        OutRbrace()
359        Output()
360
361    def outputSetter(self, name, code):
362        Output("static int %s_set_%s(%s *self, PyObject *v, void *closure)",
363            self.prefix, name, self.objecttype)
364        OutLbrace()
365        Output(code)
366        Output("return 0;")
367        OutRbrace()
368        Output()
369
370class PEP253Mixin(PEP252Mixin):
371    tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE"
372
373    def outputHook_tp_init(self):
374        Output("%s_tp_init, /* tp_init */", self.prefix)
375
376    def outputHook_tp_alloc(self):
377        Output("%s_tp_alloc, /* tp_alloc */", self.prefix)
378
379    def outputHook_tp_new(self):
380        Output("%s_tp_new, /* tp_new */", self.prefix)
381
382    def outputHook_tp_free(self):
383        Output("%s_tp_free, /* tp_free */", self.prefix)
384
385    def output_tp_initBody_basecall(self):
386        """If a type shares its init call with its base type set output_tp_initBody
387        to output_tp_initBody_basecall"""
388        if self.basetype:
389            Output("if (%s.tp_init)", self.basetype)
390            OutLbrace()
391            Output("if ( (*%s.tp_init)(_self, _args, _kwds) < 0) return -1;", self.basetype)
392            OutRbrace()
393
394    output_tp_initBody = None
395
396    def output_tp_init(self):
397        if self.output_tp_initBody:
398            Output("static int %s_tp_init(PyObject *_self, PyObject *_args, PyObject *_kwds)", self.prefix)
399            OutLbrace()
400            self.output_tp_initBody()
401            OutRbrace()
402        else:
403            Output("#define %s_tp_init 0", self.prefix)
404        Output()
405
406    output_tp_allocBody = None
407
408    def output_tp_alloc(self):
409        if self.output_tp_allocBody:
410            Output("static PyObject *%s_tp_alloc(PyTypeObject *type, int nitems)",
411                self.prefix)
412            OutLbrace()
413            self.output_tp_allocBody()
414            OutRbrace()
415        else:
416            Output("#define %s_tp_alloc PyType_GenericAlloc", self.prefix)
417        Output()
418
419    def output_tp_newBody(self):
420        Output("PyObject *_self;");
421        Output("%s itself;", self.itselftype);
422        Output("char *kw[] = {\"itself\", 0};")
423        Output()
424        Output("if (!PyArg_ParseTupleAndKeywords(_args, _kwds, \"O&\", kw, %s_Convert, &itself)) return NULL;",
425            self.prefix);
426        if self.basetype:
427            Output("if (%s.tp_new)", self.basetype)
428            OutLbrace()
429            Output("if ( (*%s.tp_new)(type, _args, _kwds) == NULL) return NULL;", self.basetype)
430            Dedent()
431            Output("} else {")
432            Indent()
433            Output("if ((_self = type->tp_alloc(type, 0)) == NULL) return NULL;")
434            OutRbrace()
435        else:
436            Output("if ((_self = type->tp_alloc(type, 0)) == NULL) return NULL;")
437        Output("((%s *)_self)->ob_itself = itself;", self.objecttype)
438        Output("return _self;")
439
440    def output_tp_new(self):
441        if self.output_tp_newBody:
442            Output("static PyObject *%s_tp_new(PyTypeObject *type, PyObject *_args, PyObject *_kwds)", self.prefix)
443            OutLbrace()
444            self.output_tp_newBody()
445            OutRbrace()
446        else:
447            Output("#define %s_tp_new PyType_GenericNew", self.prefix)
448        Output()
449
450    output_tp_freeBody = None
451
452    def output_tp_free(self):
453        if self.output_tp_freeBody:
454            Output("static void %s_tp_free(PyObject *self)", self.prefix)
455            OutLbrace()
456            self.output_tp_freeBody()
457            OutRbrace()
458        else:
459            Output("#define %s_tp_free PyObject_Del", self.prefix)
460        Output()
461
462    def outputPEP253Hooks(self):
463        self.output_tp_init()
464        self.output_tp_alloc()
465        self.output_tp_new()
466        self.output_tp_free()
467
468class GlobalObjectDefinition(ObjectDefinition):
469    """Like ObjectDefinition but exports some parts.
470
471    XXX Should also somehow generate a .h file for them.
472    """
473
474    def __init__(self, name, prefix = None, itselftype = None):
475        ObjectDefinition.__init__(self, name, prefix or name, itselftype or name)
476        self.static = ""
477
478class ObjectIdentityMixin:
479    """A mixin class for objects that makes the identity of ob_itself
480    govern comparisons and dictionary lookups. Useful if the C object can
481    be returned by library calls and it is difficult (or impossible) to find
482    the corresponding Python objects. With this you can create Python object
483    wrappers on the fly"""
484
485    def outputCompare(self):
486        Output()
487        Output("static int %s_compare(%s *self, %s *other)", self.prefix, self.objecttype,
488                self.objecttype)
489        OutLbrace()
490        Output("unsigned long v, w;")
491        Output()
492        Output("if (!%s_Check((PyObject *)other))", self.prefix)
493        OutLbrace()
494        Output("v=(unsigned long)self;")
495        Output("w=(unsigned long)other;")
496        OutRbrace()
497        Output("else")
498        OutLbrace()
499        Output("v=(unsigned long)self->ob_itself;")
500        Output("w=(unsigned long)other->ob_itself;")
501        OutRbrace()
502        Output("if( v < w ) return -1;")
503        Output("if( v > w ) return 1;")
504        Output("return 0;")
505        OutRbrace()
506
507    def outputHash(self):
508        Output()
509        Output("static long %s_hash(%s *self)", self.prefix, self.objecttype)
510        OutLbrace()
511        Output("return (long)self->ob_itself;")
512        OutRbrace()
513