Files
pybindgen/pybindgen/cppattribute.py
T

386 lines
14 KiB
Python

"""
Wraps C++ class instance/static attributes.
"""
from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase
from typehandlers import codesink
import settings
class PyGetter(ForwardWrapperBase):
"""generates a getter, for use in a PyGetSetDef table"""
def generate(self, code_sink):
"""Generate the code of the getter to the given code sink"""
raise NotImplementedError
def generate_call(self):
"""(not actually called)"""
raise AssertionError
class PySetter(ReverseWrapperBase):
"""generates a setter, for use in a PyGetSetDef table"""
def generate(self, code_sink):
"""Generate the code of the setter to the given code sink"""
raise NotImplementedError
def generate_python_call(self):
"""(not actually called)"""
raise AssertionError
class CppInstanceAttributeGetter(PyGetter):
'''
A getter for a C++ instance attribute.
'''
def __init__(self, value_type, class_, attribute_name, getter=None):
"""
value_type -- a ReturnValue object handling the value type;
class_ -- the class (CppClass object)
attribute_name -- name of attribute
getter -- None, or name of a method of the class used to get the value
"""
super(CppInstanceAttributeGetter, self).__init__(
value_type, [], "return NULL;", "return NULL;", no_c_retval=True)
self.class_ = class_
self.attribute_name = attribute_name
self.getter = getter
self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct,
self.attribute_name)
if self.getter is None:
value_type.value = "self->obj->%s" % self.attribute_name
else:
value_type.value = "self->obj->%s()" % self.getter
def generate_call(self):
"virtual method implementation; do not call"
pass
def generate(self, code_sink):
"""
code_sink -- a CodeSink instance that will receive the generated code
"""
tmp_sink = codesink.MemoryCodeSink()
self.generate_body(tmp_sink)
code_sink.writeln("static PyObject* %s(%s *self)" % (self.c_function_name,
self.class_.pystruct))
code_sink.writeln('{')
code_sink.indent()
tmp_sink.flush_to(code_sink)
code_sink.unindent()
code_sink.writeln('}')
class CppStaticAttributeGetter(PyGetter):
'''
A getter for a C++ class static attribute.
'''
def __init__(self, value_type, class_, attribute_name):
"""
value_type -- a ReturnValue object handling the value type;
c_value_expression -- C value expression
"""
super(CppStaticAttributeGetter, self).__init__(
value_type, [], "return NULL;", "return NULL;", no_c_retval=True)
self.class_ = class_
self.attribute_name = attribute_name
self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct,
self.attribute_name)
value_type.value = "%s::%s" % (self.class_.full_name, self.attribute_name)
def generate_call(self):
"virtual method implementation; do not call"
pass
def generate(self, code_sink):
"""
code_sink -- a CodeSink instance that will receive the generated code
"""
tmp_sink = codesink.MemoryCodeSink()
self.generate_body(tmp_sink)
code_sink.writeln("static PyObject* %s(void)" % self.c_function_name)
code_sink.writeln('{')
code_sink.indent()
tmp_sink.flush_to(code_sink)
code_sink.unindent()
code_sink.writeln('}')
class CppInstanceAttributeSetter(PySetter):
'''
A setter for a C++ instance attribute.
'''
def __init__(self, value_type, class_, attribute_name, setter=None):
"""
value_type -- a ReturnValue object handling the value type;
class_ -- the class (CppClass object)
attribute_name -- name of attribute
setter -- None, or name of a method of the class used to set the value
"""
super(CppInstanceAttributeSetter, self).__init__(
value_type, [], "return -1;")
self.class_ = class_
self.attribute_name = attribute_name
self.setter = setter
self.c_function_name = "_wrap_%s__set_%s" % (self.class_.pystruct,
self.attribute_name)
def generate(self, code_sink):
"""
code_sink -- a CodeSink instance that will receive the generated code
"""
self.declarations.declare_variable('PyObject*', 'py_retval')
self.before_call.write_code(
'py_retval = Py_BuildValue("(O)", value);')
self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
if self.setter is not None:
## if we have a setter method, redirect the value to a temporary variable
value_var = self.declarations.declare_variable(self.return_value.ctype, 'tmp_value')
self.return_value.value = value_var
else:
## else the value is written directly to a C++ instance attribute
self.return_value.value = "self->obj->%s" % self.attribute_name
self.return_value.convert_python_to_c(self)
parse_tuple_params = ['py_retval']
parse_tuple_params.extend(self.parse_params.get_parameters())
self.before_call.write_error_check('!PyArg_ParseTuple(%s)' %
(', '.join(parse_tuple_params),))
if self.setter is not None:
## if we have a setter method, now is the time to call it
self.after_call.write_code("self->obj->%s(%s);" % (self.setter, value_var))
## cleanup and return
self.after_call.write_cleanup()
self.after_call.write_code('return 0;')
## now generate the function itself
code_sink.writeln("static int %s(%s *self, PyObject *value)"
% (self.c_function_name, self.class_.pystruct))
code_sink.writeln('{')
code_sink.indent()
self.declarations.get_code_sink().flush_to(code_sink)
code_sink.writeln()
self.before_call.sink.flush_to(code_sink)
self.after_call.sink.flush_to(code_sink)
code_sink.unindent()
code_sink.writeln('}')
class CppStaticAttributeSetter(PySetter):
'''
A setter for a C++ class static attribute.
'''
def __init__(self, value_type, class_, attribute_name):
"""
value_type -- a ReturnValue object handling the value type;
class_ -- the class (CppClass object)
attribute_name -- name of attribute
"""
super(CppStaticAttributeSetter, self).__init__(
value_type, [], "return -1;")
self.class_ = class_
self.attribute_name = attribute_name
self.c_function_name = "_wrap_%s__set_%s" % (self.class_.pystruct,
self.attribute_name)
value_type.value = "%s::%s" % (self.class_.full_name, self.attribute_name)
def generate(self, code_sink):
"""
code_sink -- a CodeSink instance that will receive the generated code
"""
self.declarations.declare_variable('PyObject*', 'py_retval')
self.before_call.write_code(
'py_retval = Py_BuildValue("(O)", value);')
self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
self.return_value.convert_python_to_c(self)
parse_tuple_params = ['py_retval']
parse_tuple_params.extend(self.parse_params.get_parameters())
self.before_call.write_error_check('!PyArg_ParseTuple(%s)' %
(', '.join(parse_tuple_params),))
## cleanup and return
self.after_call.write_cleanup()
self.after_call.write_code('return 0;')
## now generate the function itself
code_sink.writeln(("static int %s(%s * PYBINDGEN_UNUSED(dummy), "
"PyObject *value)")
% (self.c_function_name, self.class_.pystruct))
code_sink.writeln('{')
code_sink.indent()
self.declarations.get_code_sink().flush_to(code_sink)
code_sink.writeln()
self.before_call.sink.flush_to(code_sink)
self.after_call.sink.flush_to(code_sink)
code_sink.unindent()
code_sink.writeln('}')
class PyMetaclass(object):
"""
Class that generates a Python metaclass
"""
def __init__(self, name, parent_metaclass_expr, getsets=None):
"""
name -- name of the metaclass (should normally end with Meta)
parent_metaclass_expr -- C expression that should give a
pointer to the parent metaclass
(should have a C type of
PyTypeObject*)
getsets -- name of a PyGetSetDef C array variable, or None
"""
assert getsets is None or isinstance(getsets, PyGetSetDef)
assert isinstance(name, str)
assert isinstance(parent_metaclass_expr, str)
self.name = name
prefix = settings.name_prefix.capitalize()
self.pytypestruct = "Py%s%s_Type" % (prefix, self.name)
self.parent_metaclass_expr = parent_metaclass_expr
self.getsets = getsets
def generate(self, code_sink, module):
"""
Generate the metaclass to code_sink and register it in the module.
"""
code_sink.writeln('''
PyTypeObject %(pytypestruct)s = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"%(name)s", /* tp_name */
0, /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
%(getset)s, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0 /* tp_del */
};
''' % dict(pytypestruct=self.pytypestruct, name=self.name,
getset=(self.getsets and self.getsets.cname or '0')))
module.after_init.write_code("""
%(pytypestruct)s.tp_base = %(parent_metaclass)s;
/* Some fields need to be manually inheritted from the parent metaclass */
%(pytypestruct)s.tp_traverse = %(parent_metaclass)s->tp_traverse;
%(pytypestruct)s.tp_clear = %(parent_metaclass)s->tp_clear;
%(pytypestruct)s.tp_is_gc = %(parent_metaclass)s->tp_is_gc;
/* PyType tp_setattro is too restrictive */
%(pytypestruct)s.tp_setattro = PyObject_GenericSetAttr;
PyType_Ready(&%(pytypestruct)s);
""" % dict(pytypestruct=self.pytypestruct, parent_metaclass=self.parent_metaclass_expr))
class PyGetSetDef(object):
"""
Class that generates a PyGetSet table
"""
def __init__(self, cname):
"""
cname -- C name of the getset table
"""
self.cname = cname
self.attributes = [] # (name, getter, setter)
def add_attribute(self, name, getter, setter):
"""
Add a new attribute
name -- attribute name
getter -- a PyGetter object, or None
setter -- a PySetter object, or None
"""
assert getter is None or isinstance(getter, PyGetter)
assert setter is None or isinstance(setter, PySetter)
self.attributes.append((name, getter, setter))
def generate(self, code_sink):
"""
Generate the getset table, return the table C name or '0' if
the table is empty
"""
if not self.attributes:
return '0'
for name, getter, setter in self.attributes:
if getter is not None:
getter.generate(code_sink)
if setter is not None:
setter.generate(code_sink)
code_sink.writeln("static PyGetSetDef %s[] = {" % self.cname)
code_sink.indent()
for name, getter, setter in self.attributes:
code_sink.writeln('{')
code_sink.indent()
code_sink.writeln('"%s", /* attribute name */' % name)
## getter
getter_c_name = (getter and getter.c_function_name or "NULL")
code_sink.writeln(
'(getter) %s, /* C function to get the attribute */'
% getter_c_name)
## setter
setter_c_name = setter and setter.c_function_name or "NULL"
code_sink.writeln(
'(setter) %s, /* C function to set the attribute */'
% setter_c_name)
code_sink.writeln('NULL, /* optional doc string */')
code_sink.writeln('NULL /* optional additional data '
'for getter and setter */')
code_sink.unindent()
code_sink.writeln('},')
code_sink.writeln('{ NULL, NULL, NULL, NULL, NULL }')
code_sink.unindent()
code_sink.writeln('};')
return self.cname