mirror of https://github.com/python/cpython
513 lines
17 KiB
Python
513 lines
17 KiB
Python
from bgenOutput import *
|
|
from bgenGeneratorGroup import GeneratorGroup
|
|
|
|
class ObjectDefinition(GeneratorGroup):
|
|
"Spit out code that together defines a new Python object type"
|
|
basechain = "NULL"
|
|
tp_flags = "Py_TPFLAGS_DEFAULT"
|
|
basetype = None
|
|
argref = "" # set to "*" if arg to <type>_New should be pointer
|
|
argconst = "" # set to "const " if arg to <type>_New should be const
|
|
|
|
def __init__(self, name, prefix, itselftype):
|
|
"""ObjectDefinition constructor. May be extended, but do not override.
|
|
|
|
- name: the object's official name, e.g. 'SndChannel'.
|
|
- prefix: the prefix used for the object's functions and data, e.g. 'SndCh'.
|
|
- itselftype: the C type actually contained in the object, e.g. 'SndChannelPtr'.
|
|
|
|
XXX For official Python data types, rules for the 'Py' prefix are a problem.
|
|
"""
|
|
|
|
GeneratorGroup.__init__(self, prefix or name)
|
|
self.name = name
|
|
self.itselftype = itselftype
|
|
self.objecttype = name + 'Object'
|
|
self.typename = name + '_Type'
|
|
self.static = "static " # set to "" to make <type>_New and <type>_Convert public
|
|
self.modulename = None
|
|
if hasattr(self, "assertions"):
|
|
self.assertions()
|
|
|
|
def add(self, g, dupcheck=0):
|
|
g.setselftype(self.objecttype, self.itselftype)
|
|
GeneratorGroup.add(self, g, dupcheck)
|
|
|
|
def reference(self):
|
|
# In case we are referenced from a module
|
|
pass
|
|
|
|
def setmodulename(self, name):
|
|
self.modulename = name
|
|
|
|
def generate(self):
|
|
# XXX This should use long strings and %(varname)s substitution!
|
|
|
|
OutHeader2("Object type " + self.name)
|
|
|
|
self.outputCheck()
|
|
|
|
Output("typedef struct %s {", self.objecttype)
|
|
IndentLevel()
|
|
Output("PyObject_HEAD")
|
|
self.outputStructMembers()
|
|
DedentLevel()
|
|
Output("} %s;", self.objecttype)
|
|
|
|
self.outputNew()
|
|
|
|
self.outputConvert()
|
|
|
|
self.outputDealloc()
|
|
|
|
GeneratorGroup.generate(self)
|
|
|
|
Output()
|
|
self.outputMethodChain()
|
|
|
|
self.outputGetattr()
|
|
|
|
self.outputSetattr()
|
|
|
|
self.outputCompare()
|
|
|
|
self.outputRepr()
|
|
|
|
self.outputHash()
|
|
|
|
self.outputPEP253Hooks()
|
|
|
|
self.outputTypeObject()
|
|
|
|
OutHeader2("End object type " + self.name)
|
|
|
|
def outputCheck(self):
|
|
sf = self.static and "static "
|
|
Output("%sPyTypeObject %s;", sf, self.typename)
|
|
Output()
|
|
Output("#define %s_Check(x) ((x)->ob_type == &%s || PyObject_TypeCheck((x), &%s))",
|
|
self.prefix, self.typename, self.typename)
|
|
Output()
|
|
|
|
def outputMethodChain(self):
|
|
Output("%sPyMethodChain %s_chain = { %s_methods, %s };",
|
|
self.static, self.prefix, self.prefix, self.basechain)
|
|
|
|
def outputStructMembers(self):
|
|
Output("%s ob_itself;", self.itselftype)
|
|
|
|
def outputNew(self):
|
|
Output()
|
|
Output("%sPyObject *%s_New(%s%s %sitself)", self.static, self.prefix,
|
|
self.argconst, self.itselftype, self.argref)
|
|
OutLbrace()
|
|
Output("%s *it;", self.objecttype)
|
|
self.outputCheckNewArg()
|
|
Output("it = PyObject_NEW(%s, &%s);", self.objecttype, self.typename)
|
|
Output("if (it == NULL) return NULL;")
|
|
if self.basetype:
|
|
Output("/* XXXX Should we tp_init or tp_new our basetype? */")
|
|
self.outputInitStructMembers()
|
|
Output("return (PyObject *)it;")
|
|
OutRbrace()
|
|
|
|
def outputInitStructMembers(self):
|
|
Output("it->ob_itself = %sitself;", self.argref)
|
|
|
|
def outputCheckNewArg(self):
|
|
"Override this method to apply additional checks/conversions"
|
|
|
|
def outputConvert(self):
|
|
Output()
|
|
Output("%sint %s_Convert(PyObject *v, %s *p_itself)", self.static, self.prefix,
|
|
self.itselftype)
|
|
OutLbrace()
|
|
self.outputCheckConvertArg()
|
|
Output("if (!%s_Check(v))", self.prefix)
|
|
OutLbrace()
|
|
Output('PyErr_SetString(PyExc_TypeError, "%s required");', self.name)
|
|
Output("return 0;")
|
|
OutRbrace()
|
|
Output("*p_itself = ((%s *)v)->ob_itself;", self.objecttype)
|
|
Output("return 1;")
|
|
OutRbrace()
|
|
|
|
def outputCheckConvertArg(self):
|
|
"Override this method to apply additional conversions"
|
|
|
|
def outputDealloc(self):
|
|
Output()
|
|
Output("static void %s_dealloc(%s *self)", self.prefix, self.objecttype)
|
|
OutLbrace()
|
|
self.outputCleanupStructMembers()
|
|
if self.basetype:
|
|
Output("%s.tp_dealloc((PyObject *)self);", self.basetype)
|
|
elif hasattr(self, 'output_tp_free'):
|
|
# This is a new-style object with tp_free slot
|
|
Output("self->ob_type->tp_free((PyObject *)self);")
|
|
else:
|
|
Output("PyObject_Free((PyObject *)self);")
|
|
OutRbrace()
|
|
|
|
def outputCleanupStructMembers(self):
|
|
self.outputFreeIt("self->ob_itself")
|
|
|
|
def outputFreeIt(self, name):
|
|
Output("/* Cleanup of %s goes here */", name)
|
|
|
|
def outputGetattr(self):
|
|
Output()
|
|
Output("static PyObject *%s_getattr(%s *self, char *name)", self.prefix, self.objecttype)
|
|
OutLbrace()
|
|
self.outputGetattrBody()
|
|
OutRbrace()
|
|
|
|
def outputGetattrBody(self):
|
|
self.outputGetattrHook()
|
|
Output("return Py_FindMethodInChain(&%s_chain, (PyObject *)self, name);",
|
|
self.prefix)
|
|
|
|
def outputGetattrHook(self):
|
|
pass
|
|
|
|
def outputSetattr(self):
|
|
Output()
|
|
Output("#define %s_setattr NULL", self.prefix)
|
|
|
|
def outputCompare(self):
|
|
Output()
|
|
Output("#define %s_compare NULL", self.prefix)
|
|
|
|
def outputRepr(self):
|
|
Output()
|
|
Output("#define %s_repr NULL", self.prefix)
|
|
|
|
def outputHash(self):
|
|
Output()
|
|
Output("#define %s_hash NULL", self.prefix)
|
|
|
|
def outputTypeObject(self):
|
|
sf = self.static and "static "
|
|
Output()
|
|
Output("%sPyTypeObject %s = {", sf, self.typename)
|
|
IndentLevel()
|
|
Output("PyObject_HEAD_INIT(NULL)")
|
|
Output("0, /*ob_size*/")
|
|
if self.modulename:
|
|
Output("\"%s.%s\", /*tp_name*/", self.modulename, self.name)
|
|
else:
|
|
Output("\"%s\", /*tp_name*/", self.name)
|
|
Output("sizeof(%s), /*tp_basicsize*/", self.objecttype)
|
|
Output("0, /*tp_itemsize*/")
|
|
Output("/* methods */")
|
|
Output("(destructor) %s_dealloc, /*tp_dealloc*/", self.prefix)
|
|
Output("0, /*tp_print*/")
|
|
Output("(getattrfunc) %s_getattr, /*tp_getattr*/", self.prefix)
|
|
Output("(setattrfunc) %s_setattr, /*tp_setattr*/", self.prefix)
|
|
Output("(cmpfunc) %s_compare, /*tp_compare*/", self.prefix)
|
|
Output("(reprfunc) %s_repr, /*tp_repr*/", self.prefix)
|
|
Output("(PyNumberMethods *)0, /* tp_as_number */")
|
|
Output("(PySequenceMethods *)0, /* tp_as_sequence */")
|
|
Output("(PyMappingMethods *)0, /* tp_as_mapping */")
|
|
Output("(hashfunc) %s_hash, /*tp_hash*/", self.prefix)
|
|
DedentLevel()
|
|
Output("};")
|
|
|
|
def outputTypeObjectInitializer(self):
|
|
Output("""%s.ob_type = &PyType_Type;""", self.typename)
|
|
if self.basetype:
|
|
Output("%s.tp_base = &%s;", self.typename, self.basetype)
|
|
Output("if (PyType_Ready(&%s) < 0) return;", self.typename)
|
|
Output("""Py_INCREF(&%s);""", self.typename)
|
|
Output("PyModule_AddObject(m, \"%s\", (PyObject *)&%s);", self.name, self.typename);
|
|
self.outputTypeObjectInitializerCompat()
|
|
|
|
def outputTypeObjectInitializerCompat(self):
|
|
Output("/* Backward-compatible name */")
|
|
Output("""Py_INCREF(&%s);""", self.typename);
|
|
Output("PyModule_AddObject(m, \"%sType\", (PyObject *)&%s);", self.name, self.typename);
|
|
|
|
def outputPEP253Hooks(self):
|
|
pass
|
|
|
|
class PEP252Mixin:
|
|
getsetlist = []
|
|
|
|
def assertions(self):
|
|
# Check that various things aren't overridden. If they are it could
|
|
# signify a bgen-client that has been partially converted to PEP252.
|
|
assert self.outputGetattr.im_func == PEP252Mixin.outputGetattr.im_func
|
|
assert self.outputSetattr.im_func == PEP252Mixin.outputSetattr.im_func
|
|
assert self.outputGetattrBody == None
|
|
assert self.outputGetattrHook == None
|
|
assert self.basechain == "NULL"
|
|
|
|
def outputGetattr(self):
|
|
pass
|
|
|
|
outputGetattrBody = None
|
|
|
|
outputGetattrHook = None
|
|
|
|
def outputSetattr(self):
|
|
pass
|
|
|
|
def outputMethodChain(self):
|
|
# This is a good place to output the getters and setters
|
|
self.outputGetSetList()
|
|
|
|
def outputHook(self, name):
|
|
methodname = "outputHook_" + name
|
|
if hasattr(self, methodname):
|
|
func = getattr(self, methodname)
|
|
func()
|
|
else:
|
|
Output("0, /*%s*/", name)
|
|
|
|
def outputTypeObject(self):
|
|
sf = self.static and "static "
|
|
Output()
|
|
Output("%sPyTypeObject %s = {", sf, self.typename)
|
|
IndentLevel()
|
|
Output("PyObject_HEAD_INIT(NULL)")
|
|
Output("0, /*ob_size*/")
|
|
if self.modulename:
|
|
Output("\"%s.%s\", /*tp_name*/", self.modulename, self.name)
|
|
else:
|
|
Output("\"%s\", /*tp_name*/", self.name)
|
|
Output("sizeof(%s), /*tp_basicsize*/", self.objecttype)
|
|
Output("0, /*tp_itemsize*/")
|
|
|
|
Output("/* methods */")
|
|
Output("(destructor) %s_dealloc, /*tp_dealloc*/", self.prefix)
|
|
Output("0, /*tp_print*/")
|
|
Output("(getattrfunc)0, /*tp_getattr*/")
|
|
Output("(setattrfunc)0, /*tp_setattr*/")
|
|
Output("(cmpfunc) %s_compare, /*tp_compare*/", self.prefix)
|
|
Output("(reprfunc) %s_repr, /*tp_repr*/", self.prefix)
|
|
|
|
Output("(PyNumberMethods *)0, /* tp_as_number */")
|
|
Output("(PySequenceMethods *)0, /* tp_as_sequence */")
|
|
Output("(PyMappingMethods *)0, /* tp_as_mapping */")
|
|
|
|
Output("(hashfunc) %s_hash, /*tp_hash*/", self.prefix)
|
|
self.outputHook("tp_call")
|
|
Output("0, /*tp_str*/")
|
|
Output("PyObject_GenericGetAttr, /*tp_getattro*/")
|
|
Output("PyObject_GenericSetAttr, /*tp_setattro */")
|
|
|
|
self.outputHook("tp_as_buffer")
|
|
Output("%s, /* tp_flags */", self.tp_flags)
|
|
self.outputHook("tp_doc")
|
|
self.outputHook("tp_traverse")
|
|
self.outputHook("tp_clear")
|
|
self.outputHook("tp_richcompare")
|
|
self.outputHook("tp_weaklistoffset")
|
|
self.outputHook("tp_iter")
|
|
self.outputHook("tp_iternext")
|
|
Output("%s_methods, /* tp_methods */", self.prefix)
|
|
self.outputHook("tp_members")
|
|
Output("%s_getsetlist, /*tp_getset*/", self.prefix)
|
|
self.outputHook("tp_base")
|
|
self.outputHook("tp_dict")
|
|
self.outputHook("tp_descr_get")
|
|
self.outputHook("tp_descr_set")
|
|
self.outputHook("tp_dictoffset")
|
|
self.outputHook("tp_init")
|
|
self.outputHook("tp_alloc")
|
|
self.outputHook("tp_new")
|
|
self.outputHook("tp_free")
|
|
DedentLevel()
|
|
Output("};")
|
|
|
|
def outputGetSetList(self):
|
|
if self.getsetlist:
|
|
for name, get, set, doc in self.getsetlist:
|
|
if get:
|
|
self.outputGetter(name, get)
|
|
else:
|
|
Output("#define %s_get_%s NULL", self.prefix, name)
|
|
Output()
|
|
if set:
|
|
self.outputSetter(name, set)
|
|
else:
|
|
Output("#define %s_set_%s NULL", self.prefix, name)
|
|
Output()
|
|
|
|
Output("static PyGetSetDef %s_getsetlist[] = {", self.prefix)
|
|
IndentLevel()
|
|
for name, get, set, doc in self.getsetlist:
|
|
if doc:
|
|
doc = '"' + doc + '"'
|
|
else:
|
|
doc = "NULL"
|
|
Output("{\"%s\", (getter)%s_get_%s, (setter)%s_set_%s, %s},",
|
|
name, self.prefix, name, self.prefix, name, doc)
|
|
Output("{NULL, NULL, NULL, NULL},")
|
|
DedentLevel()
|
|
Output("};")
|
|
else:
|
|
Output("#define %s_getsetlist NULL", self.prefix)
|
|
Output()
|
|
|
|
def outputGetter(self, name, code):
|
|
Output("static PyObject *%s_get_%s(%s *self, void *closure)",
|
|
self.prefix, name, self.objecttype)
|
|
OutLbrace()
|
|
Output(code)
|
|
OutRbrace()
|
|
Output()
|
|
|
|
def outputSetter(self, name, code):
|
|
Output("static int %s_set_%s(%s *self, PyObject *v, void *closure)",
|
|
self.prefix, name, self.objecttype)
|
|
OutLbrace()
|
|
Output(code)
|
|
Output("return 0;")
|
|
OutRbrace()
|
|
Output()
|
|
|
|
class PEP253Mixin(PEP252Mixin):
|
|
tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE"
|
|
|
|
def outputHook_tp_init(self):
|
|
Output("%s_tp_init, /* tp_init */", self.prefix)
|
|
|
|
def outputHook_tp_alloc(self):
|
|
Output("%s_tp_alloc, /* tp_alloc */", self.prefix)
|
|
|
|
def outputHook_tp_new(self):
|
|
Output("%s_tp_new, /* tp_new */", self.prefix)
|
|
|
|
def outputHook_tp_free(self):
|
|
Output("%s_tp_free, /* tp_free */", self.prefix)
|
|
|
|
def output_tp_initBody_basecall(self):
|
|
"""If a type shares its init call with its base type set output_tp_initBody
|
|
to output_tp_initBody_basecall"""
|
|
if self.basetype:
|
|
Output("if (%s.tp_init)", self.basetype)
|
|
OutLbrace()
|
|
Output("if ( (*%s.tp_init)(_self, _args, _kwds) < 0) return -1;", self.basetype)
|
|
OutRbrace()
|
|
|
|
output_tp_initBody = None
|
|
|
|
def output_tp_init(self):
|
|
if self.output_tp_initBody:
|
|
Output("static int %s_tp_init(PyObject *_self, PyObject *_args, PyObject *_kwds)", self.prefix)
|
|
OutLbrace()
|
|
self.output_tp_initBody()
|
|
OutRbrace()
|
|
else:
|
|
Output("#define %s_tp_init 0", self.prefix)
|
|
Output()
|
|
|
|
output_tp_allocBody = None
|
|
|
|
def output_tp_alloc(self):
|
|
if self.output_tp_allocBody:
|
|
Output("static PyObject *%s_tp_alloc(PyTypeObject *type, int nitems)",
|
|
self.prefix)
|
|
OutLbrace()
|
|
self.output_tp_allocBody()
|
|
OutRbrace()
|
|
else:
|
|
Output("#define %s_tp_alloc PyType_GenericAlloc", self.prefix)
|
|
Output()
|
|
|
|
def output_tp_newBody(self):
|
|
Output("PyObject *_self;");
|
|
Output("%s itself;", self.itselftype);
|
|
Output("char *kw[] = {\"itself\", 0};")
|
|
Output()
|
|
Output("if (!PyArg_ParseTupleAndKeywords(_args, _kwds, \"O&\", kw, %s_Convert, &itself)) return NULL;",
|
|
self.prefix);
|
|
if self.basetype:
|
|
Output("if (%s.tp_new)", self.basetype)
|
|
OutLbrace()
|
|
Output("if ( (*%s.tp_new)(type, _args, _kwds) == NULL) return NULL;", self.basetype)
|
|
Dedent()
|
|
Output("} else {")
|
|
Indent()
|
|
Output("if ((_self = type->tp_alloc(type, 0)) == NULL) return NULL;")
|
|
OutRbrace()
|
|
else:
|
|
Output("if ((_self = type->tp_alloc(type, 0)) == NULL) return NULL;")
|
|
Output("((%s *)_self)->ob_itself = itself;", self.objecttype)
|
|
Output("return _self;")
|
|
|
|
def output_tp_new(self):
|
|
if self.output_tp_newBody:
|
|
Output("static PyObject *%s_tp_new(PyTypeObject *type, PyObject *_args, PyObject *_kwds)", self.prefix)
|
|
OutLbrace()
|
|
self.output_tp_newBody()
|
|
OutRbrace()
|
|
else:
|
|
Output("#define %s_tp_new PyType_GenericNew", self.prefix)
|
|
Output()
|
|
|
|
output_tp_freeBody = None
|
|
|
|
def output_tp_free(self):
|
|
if self.output_tp_freeBody:
|
|
Output("static void %s_tp_free(PyObject *self)", self.prefix)
|
|
OutLbrace()
|
|
self.output_tp_freeBody()
|
|
OutRbrace()
|
|
else:
|
|
Output("#define %s_tp_free PyObject_Del", self.prefix)
|
|
Output()
|
|
|
|
def outputPEP253Hooks(self):
|
|
self.output_tp_init()
|
|
self.output_tp_alloc()
|
|
self.output_tp_new()
|
|
self.output_tp_free()
|
|
|
|
class GlobalObjectDefinition(ObjectDefinition):
|
|
"""Like ObjectDefinition but exports some parts.
|
|
|
|
XXX Should also somehow generate a .h file for them.
|
|
"""
|
|
|
|
def __init__(self, name, prefix = None, itselftype = None):
|
|
ObjectDefinition.__init__(self, name, prefix or name, itselftype or name)
|
|
self.static = ""
|
|
|
|
class ObjectIdentityMixin:
|
|
"""A mixin class for objects that makes the identity of ob_itself
|
|
govern comparisons and dictionary lookups. Useful if the C object can
|
|
be returned by library calls and it is difficult (or impossible) to find
|
|
the corresponding Python objects. With this you can create Python object
|
|
wrappers on the fly"""
|
|
|
|
def outputCompare(self):
|
|
Output()
|
|
Output("static int %s_compare(%s *self, %s *other)", self.prefix, self.objecttype,
|
|
self.objecttype)
|
|
OutLbrace()
|
|
Output("unsigned long v, w;")
|
|
Output()
|
|
Output("if (!%s_Check((PyObject *)other))", self.prefix)
|
|
OutLbrace()
|
|
Output("v=(unsigned long)self;")
|
|
Output("w=(unsigned long)other;")
|
|
OutRbrace()
|
|
Output("else")
|
|
OutLbrace()
|
|
Output("v=(unsigned long)self->ob_itself;")
|
|
Output("w=(unsigned long)other->ob_itself;")
|
|
OutRbrace()
|
|
Output("if( v < w ) return -1;")
|
|
Output("if( v > w ) return 1;")
|
|
Output("return 0;")
|
|
OutRbrace()
|
|
|
|
def outputHash(self):
|
|
Output()
|
|
Output("static long %s_hash(%s *self)", self.prefix, self.objecttype)
|
|
OutLbrace()
|
|
Output("return (long)self->ob_itself;")
|
|
OutRbrace()
|