From 074b9b2413564f35553852955e6f1f8ff48058bb Mon Sep 17 00:00:00 2001 From: Samuli Tuomola Date: Fri, 25 May 2018 20:44:49 +0300 Subject: [PATCH 1/3] Adds support for Py_LIMITED_API --- pybindgen/container.py | 50 ++++++++++++-------- pybindgen/cppclass.py | 81 ++++++++++++++++++++------------- pybindgen/cppclass_container.py | 8 +++- pybindgen/enum.py | 7 ++- pybindgen/overloading.py | 2 +- pybindgen/pytypeobject.py | 28 ++++++++++++ pybindgen/utils.py | 20 +++++++- 7 files changed, 142 insertions(+), 54 deletions(-) diff --git a/pybindgen/container.py b/pybindgen/container.py index c2616aa..785b99f 100644 --- a/pybindgen/container.py +++ b/pybindgen/container.py @@ -308,8 +308,8 @@ def generate_forward_declarations(self, code_sink, module): ''' % (self.pystruct, self.full_name, self.iter_pystruct)) code_sink.writeln() - code_sink.writeln('extern PyTypeObject %s;' % (self.pytypestruct,)) - code_sink.writeln('extern PyTypeObject %s;' % (self.iter_pytypestruct,)) + code_sink.writeln('extern PyTypeObject _TYPEDEC %s;' % (self.pytypestruct,)) + code_sink.writeln('extern PyTypeObject _TYPEDEC %s;' % (self.iter_pytypestruct,)) code_sink.writeln() this_type_converter = self.module.get_root().get_python_to_c_type_converter_function_name( @@ -346,18 +346,25 @@ def generate(self, code_sink, module, docstring=None): ## --- register the class type in the module --- module.after_init.write_code("/* Register the '%s' class */" % self.full_name) + #import traceback; module.after_init.write_code(repr(traceback.format_stack())) - module.after_init.write_error_check('PyType_Ready(&%s)' % (self.pytypestruct,)) - module.after_init.write_error_check('PyType_Ready(&%s)' % (self.iter_pytypestruct,)) + module.after_init.write_code('#ifdef Py_LIMITED_API\n' + '%s = (PyTypeObject*)PyType_FromSpec(&%s_spec);\n' + '%s = (PyTypeObject*)PyType_FromSpec(&%s_spec);\n' + '#endif' % (self.pytypestruct, self.pytypestruct, + self.iter_pytypestruct, self.iter_pytypestruct)) + + module.after_init.write_error_check('PyType_Ready(_TYPEREF %s)' % (self.pytypestruct,)) + module.after_init.write_error_check('PyType_Ready(_TYPEREF %s)' % (self.iter_pytypestruct,)) class_python_name = self.python_name if self.outer_class is None: module.after_init.write_code( - 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % ( + 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) _TYPEREF %s);' % ( class_python_name, self.pytypestruct)) module.after_init.write_code( - 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % ( + 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) _TYPEREF %s);' % ( class_python_name+'Iter', self.iter_pytypestruct)) else: module.after_init.write_code( @@ -438,10 +445,13 @@ def _generate_destructor(self, code_sink): %s(%s *self) { %s + #ifdef Py_LIMITED_API + PyObject_DEL(self); + #else Py_TYPE(self)->tp_free((PyObject*)self); + #endif } -''' % (container_tp_dealloc_function_name, self.pystruct, - self._get_container_delete_code())) +''' % (container_tp_dealloc_function_name, self.pystruct, self._get_container_delete_code())) self.pytype.slots.setdefault("tp_dealloc", container_tp_dealloc_function_name ) @@ -454,7 +464,11 @@ def _generate_destructor(self, code_sink): { Py_CLEAR(self->container); %s + #ifdef Py_LIMITED_API + PyObject_DEL(self); + #else Py_TYPE(self)->tp_free((PyObject*)self); + #endif } ''' % (iter_tp_dealloc_function_name, self.iter_pystruct, self._get_iter_delete_code())) @@ -478,7 +492,7 @@ def _generate_iter_methods(self, code_sink): static PyObject* %(CONTAINER_ITER_FUNC)s(%(PYSTRUCT)s *self) { - %(ITER_PYSTRUCT)s *iter = PyObject_GC_New(%(ITER_PYSTRUCT)s, &%(ITER_PYTYPESTRUCT)s); + %(ITER_PYSTRUCT)s *iter = PyObject_GC_New(%(ITER_PYSTRUCT)s, _TYPEREF %(ITER_PYTYPESTRUCT)s); Py_INCREF(self); iter->container = self; iter->iterator = new %(CTYPE)s::iterator(self->obj->begin()); @@ -542,7 +556,7 @@ def _generate_container_constructor(self, code_sink): Py_ssize_t size = PyList_Size(arg); for (Py_ssize_t i = 0; i < size; i++) { %(ITEM_CTYPE)s item; - if (!%(ITEM_CONVERTER)s(PyList_GET_ITEM(arg, i), &item)) { + if (!%(ITEM_CONVERTER)s(PyList_GetItem(arg, i), &item)) { return 0; } container->%(ADD_VALUE)s(item); @@ -573,16 +587,16 @@ def _generate_container_constructor(self, code_sink): container->clear(); Py_ssize_t size = PyList_Size(arg); for (Py_ssize_t i = 0; i < size; i++) { - PyObject *tup = PyList_GET_ITEM(arg, i); + PyObject *tup = PyList_GetItem(arg, i); if (!PyTuple_Check(tup) || PyTuple_Size(tup) != 2) { PyErr_SetString(PyExc_TypeError, "items must be tuples with two elements"); return 0; } std::pair< %(KEY_CTYPE)s, %(ITEM_CTYPE)s > item; - if (!%(KEY_CONVERTER)s(PyTuple_GET_ITEM(tup, 0), &item.first)) { + if (!%(KEY_CONVERTER)s(PyTuple_GetItem(tup, 0), &item.first)) { return 0; } - if (!%(ITEM_CONVERTER)s(PyTuple_GET_ITEM(tup, 1), &item.second)) { + if (!%(ITEM_CONVERTER)s(PyTuple_GetItem(tup, 1), &item.second)) { return 0; } container->%(ADD_VALUE)s(item); @@ -693,7 +707,7 @@ def convert_c_to_python(self, wrapper): self.container_type.pystruct+'*', 'py_'+self.container_type.name) wrapper.before_call.write_code( "%s = PyObject_New(%s, %s);" % - (self.py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct)) + (self.py_name, self.container_type.pystruct, '_TYPEREF '+self.container_type.pytypestruct)) wrapper.before_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.value)) @@ -728,7 +742,7 @@ def convert_python_to_c(self, wrapper): self.container_type.pystruct+'*', 'py_'+self.container_type.name) wrapper.after_call.write_code( "%s = PyObject_New(%s, %s);" % - (py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct)) + (py_name, self.container_type.pystruct, '_TYPEREF '+self.container_type.pytypestruct)) wrapper.after_call.write_code("%s->obj = new %s(%s);" % (py_name, self.container_type.full_name, container_tmp_var)) wrapper.build_params.add_parameter("N", [py_name]) @@ -740,7 +754,7 @@ def convert_c_to_python(self, wrapper): self.container_type.pystruct+'*', 'py_'+self.container_type.name) wrapper.before_call.write_code( "%s = PyObject_New(%s, %s);" % - (self.py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct)) + (self.py_name, self.container_type.pystruct, '_TYPEREF '+self.container_type.pytypestruct)) if self.direction & Parameter.DIRECTION_IN: wrapper.before_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.name)) @@ -800,7 +814,7 @@ def convert_python_to_c(self, wrapper): wrapper.after_call.write_code( "%s = PyObject_New(%s, %s);" % - (py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct)) + (py_name, self.container_type.pystruct, '_TYPEREF '+self.container_type.pytypestruct)) wrapper.after_call.write_code("%s->obj = %s;" % (py_name, container_tmp_var)) @@ -855,7 +869,7 @@ def convert_c_to_python(self, wrapper): wrapper.after_call.write_code( "%s = PyObject_New(%s, %s);" % - (py_name, self.container_type.pystruct, '&'+self.container_type.pytypestruct)) + (py_name, self.container_type.pystruct, '_TYPEREF '+self.container_type.pytypestruct)) wrapper.after_call.write_code("%s->obj = new %s(%s);" % (self.py_name, self.container_type.full_name, self.value)) wrapper.build_params.add_parameter("N", [py_name], prepend=True) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 1971151..7cb1211 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -1839,7 +1839,7 @@ def generate_forward_declarations(self, code_sink, module): code_sink.writeln('extern PyTypeObject *_%s;' % (self.pytypestruct,)) code_sink.writeln('#define %s (*_%s)' % (self.pytypestruct, self.pytypestruct)) else: - code_sink.writeln('extern PyTypeObject %s;' % (self.pytypestruct,)) + code_sink.writeln('extern PyTypeObject _TYPEDEC %s;' % (self.pytypestruct,)) if not self.static_attributes.empty(): code_sink.writeln('extern PyTypeObject Py%s_Type;' % (self.metaclass_name,)) @@ -1971,32 +1971,46 @@ def generate(self, code_sink, module): if self.parent is not None: assert isinstance(self.parent, CppClass) - module.after_init.write_code('%s.tp_base = &%s;' % - (self.pytypestruct, self.parent.pytypestruct)) + module.after_init.write_code( + '#ifdef Py_LIMITED_API\n' + '%s = (PyTypeObject*)PyType_FromSpec(&%s_spec);\n' + '#else\n' + '%s.tp_base = &%s;\n' + '#endif\n' % (self.pytypestruct, self.pytypestruct, self.pytypestruct, self.parent.pytypestruct)) if len(self.bases) > 1: module.after_init.write_code('%s.tp_bases = PyTuple_New(%i);' % (self.pytypestruct, len(self.bases),)) for basenum, base in enumerate(self.bases): module.after_init.write_code(' Py_INCREF((PyObject *) &%s);' % (base.pytypestruct,)) - module.after_init.write_code(' PyTuple_SET_ITEM(%s.tp_bases, %i, (PyObject *) &%s);' - % (self.pytypestruct, basenum, base.pytypestruct)) + module.after_init.write_code(' PyTuple_SetItem(%s.tp_bases, %i, (PyObject *) &%s);' + % (self.pytypestruct, basenum, base.pytypestruct)) + else: + module.after_init.write_code( + '#ifdef Py_LIMITED_API\n' + '%s = (PyTypeObject*)PyType_FromSpec(&%s_spec);\n' + '#endif\n' % (self.pytypestruct, self.pytypestruct)) if metaclass is not None: module.after_init.write_code('Py_TYPE(&%s) = &%s;' % (self.pytypestruct, metaclass.pytypestruct)) - module.after_init.write_error_check('PyType_Ready(&%s)' - % (self.pytypestruct,)) + # TODO: not needed with Py_LIMITED_API? + module.after_init.write_error_check('PyType_Ready(_TYPEREF %s)' + % (self.pytypestruct,)) class_python_name = self.get_python_name() if self.outer_class is None: module.after_init.write_code( - 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) &%s);' % ( + 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) _TYPEREF %s);' % ( class_python_name, self.pytypestruct)) else: module.after_init.write_code( - 'PyDict_SetItemString((PyObject*) %s.tp_dict, (char *) \"%s\", (PyObject *) &%s);' % ( - self.outer_class.pytypestruct, class_python_name, self.pytypestruct)) + '#ifdef Py_LIMITED_API\n' + 'PyObject_SetAttrString((PyObject*) %s, (char *) \"%s\", (PyObject *) %s);\n' + '#else\n' + 'PyDict_SetItemString((PyObject*) %s.tp_dict, \"%s\", (PyObject *) &%s);\n' + '#endif\n' % (self.outer_class.pytypestruct, class_python_name, self.pytypestruct, + self.outer_class.pytypestruct, class_python_name, self.pytypestruct)) have_constructor = self._generate_constructor(code_sink) @@ -2564,7 +2578,12 @@ def _generate_destructor(self, code_sink, have_constructor): else: code_block.write_code(self._get_delete_code()) - code_block.write_code('Py_TYPE(self)->tp_free((PyObject*)self);') + code_block.write_code( + ' #ifdef Py_LIMITED_API\n' + ' PyObject_DEL(self);\n' + ' #else\n' + ' Py_TYPE(self)->tp_free((PyObject*)self);\n' + ' #endif\n') code_block.write_cleanup() @@ -2586,7 +2605,7 @@ def _generate_tp_richcompare(self, code_sink): code_sink.indent() code_sink.writeln(""" -if (!PyObject_IsInstance((PyObject*) other, (PyObject*) &%s)) { +if (!PyObject_IsInstance((PyObject*) other, (PyObject*) _TYPEREF %s)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; }""" % self.pytypestruct) @@ -2646,7 +2665,7 @@ def write_allocate_pystruct(self, code_block, lvalue, wrapper_type=None): else: new_func = 'PyObject_New' if wrapper_type is None: - wrapper_type = '&'+self.pytypestruct + wrapper_type = '_TYPEREF ' + self.pytypestruct code_block.write_code("%s = %s(%s, %s);" % (lvalue, new_func, self.pystruct, wrapper_type)) if self.allow_subclassing: @@ -2700,7 +2719,7 @@ def write_create_new_wrapper(): else: - wrapper_type = '&'+cpp_class.pytypestruct + wrapper_type = '_TYPEREF '+cpp_class.pytypestruct ## Create the Python wrapper object cpp_class.write_allocate_pystruct(code_block, py_name, wrapper_type) @@ -2944,14 +2963,14 @@ def convert_python_to_c(self, wrapper): self.py_name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', self.name, 'NULL') wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True) wrapper.call_params.append( '(%s ? (*((%s *) %s)->obj) : %s)' % (self.py_name, self.cpp_class.pystruct, self.py_name, self.default_value)) else: self.py_name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', self.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) wrapper.call_params.append( '*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name)) else: @@ -2969,7 +2988,7 @@ def convert_python_to_c(self, wrapper): wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name, optional=True) if self.default_value is None: - wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n" + wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) _TYPEREF %s)) {\n" " %s = *((%s *) %s)->obj;" % (self.py_name, self.cpp_class.pytypestruct, tmp_value_variable, @@ -2980,13 +2999,13 @@ def convert_python_to_c(self, wrapper): " %s = %s;" % (self.py_name, tmp_value_variable, self.default_value)) wrapper.before_call.write_code( - "} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n" + "} else if (PyObject_IsInstance(%s, (PyObject*) _TYPEREF %s)) {\n" " %s = *((%s *) %s)->obj;" % (self.py_name, self.cpp_class.pytypestruct, tmp_value_variable, self.cpp_class.pystruct, self.py_name)) for conversion_source in implicit_conversion_sources: - wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n" + wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) _TYPEREF %s)) {\n" " %s = *((%s *) %s)->obj;" % (self.py_name, conversion_source.pytypestruct, tmp_value_variable, @@ -3063,7 +3082,7 @@ def convert_python_to_c(self, wrapper): self.cpp_class.pystruct+'*', self.name, 'NULL') wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=True) if self.default_value_type is not None: default_value_name = wrapper.declarations.declare_variable( @@ -3081,7 +3100,7 @@ def convert_python_to_c(self, wrapper): self.py_name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', self.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) wrapper.call_params.append( '*((%s *) %s)->obj' % (self.cpp_class.pystruct, self.py_name)) else: @@ -3094,13 +3113,13 @@ def convert_python_to_c(self, wrapper): self.cpp_class.full_name, self.name) wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name) - wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n" + wrapper.before_call.write_code("if (PyObject_IsInstance(%s, (PyObject*) _TYPEREF %s)) {\n" " %s = *((%s *) %s)->obj;" % (self.py_name, self.cpp_class.pytypestruct, tmp_value_variable, self.cpp_class.pystruct, self.py_name)) for conversion_source in implicit_conversion_sources: - wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) &%s)) {\n" + wrapper.before_call.write_code("} else if (PyObject_IsInstance(%s, (PyObject*) _TYPEREF %s)) {\n" " %s = *((%s *) %s)->obj;" % (self.py_name, conversion_source.pytypestruct, tmp_value_variable, @@ -3151,7 +3170,7 @@ def convert_python_to_c(self, wrapper): self.cpp_class.pystruct+'*', self.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name) wrapper.call_params.append( '*%s->obj' % (self.py_name)) @@ -3271,7 +3290,7 @@ def convert_python_to_c(self, wrapper): name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+name]) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+name]) if self.REQUIRES_ASSIGNMENT_CONSTRUCTOR: wrapper.after_call.write_code('%s %s = *%s->obj;' % (self.cpp_class.full_name, self.value, name)) @@ -3346,7 +3365,7 @@ def convert_python_to_c(self, wrapper): name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+name]) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+name]) if self.REQUIRES_ASSIGNMENT_CONSTRUCTOR: wrapper.after_call.write_code('%s %s = *%s->obj;' % (self.cpp_class.full_name, self.value, name)) @@ -3441,7 +3460,7 @@ def convert_python_to_c(self, wrapper): wrapper.before_call.write_error_check( - "%s && ((PyObject *) %s != Py_None) && !PyObject_IsInstance((PyObject *) %s, (PyObject *) &%s)" + "%s && ((PyObject *) %s != Py_None) && !PyObject_IsInstance((PyObject *) %s, (PyObject *) _TYPEREF %s)" % (self.py_name, self.py_name, self.py_name, self.cpp_class.pytypestruct), 'PyErr_SetString(PyExc_TypeError, "Parameter %i must be of type %s");' % (num, self.cpp_class.name)) @@ -3458,7 +3477,7 @@ def convert_python_to_c(self, wrapper): else: wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=bool(self.default_value)) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=bool(self.default_value)) wrapper.before_call.write_code("%s = (%s ? %s->obj : NULL);" % (value_ptr, self.py_name, self.py_name)) value = self.transformation.transform(self, wrapper.declarations, wrapper.before_call, value_ptr) @@ -3514,10 +3533,10 @@ def write_create_new_wrapper(): wrapper_type = wrapper.declarations.declare_variable( 'PyTypeObject*', 'wrapper_type', '0') wrapper.before_call.write_code( - '%s = %s.lookup_wrapper(typeid(*%s), &%s);' + '%s = %s.lookup_wrapper(typeid(*%s), _TYPEREF %s);' % (wrapper_type, typeid_map_name, value, self.cpp_class.pytypestruct)) else: - wrapper_type = '&'+self.cpp_class.pytypestruct + wrapper_type = '_TYPEREF '+self.cpp_class.pytypestruct ## Create the Python wrapper object self.cpp_class.write_allocate_pystruct(wrapper.before_call, py_name, wrapper_type) @@ -3770,7 +3789,7 @@ def convert_python_to_c(self, wrapper): name = wrapper.declarations.declare_variable( self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name) wrapper.parse_params.add_parameter( - 'O!', ['&'+self.cpp_class.pytypestruct, '&'+name]) + 'O!', ['_TYPEREF '+self.cpp_class.pytypestruct, '&'+name]) value = self.transformation.transform( self, wrapper.declarations, wrapper.after_call, "%s->obj" % name) diff --git a/pybindgen/cppclass_container.py b/pybindgen/cppclass_container.py index 7c22320..09c7653 100644 --- a/pybindgen/cppclass_container.py +++ b/pybindgen/cppclass_container.py @@ -135,7 +135,11 @@ def generate(self, code_sink, module, docstring=None): ## --- register the iter type in the module --- module.after_init.write_code("/* Register the '%s' class iterator*/" % self.cppclass.full_name) - module.after_init.write_error_check('PyType_Ready(&%s)' % (self.iter_pytypestruct,)) + module.after_init.write_error_check('PyType_Ready(_TYPEREF %s)' % (self.iter_pytypestruct,)) + module.after_init.write_code('#ifdef Py_LIMITED_API') + module.after_init.write_code('%s = (PyTypeObject*)PyType_FromSpec(&%s_spec);\n' + % (self.iter_pytypestruct, self.iter_pytypestruct)) + module.after_init.write_code('#endif') if self.cppclass.outer_class is None: module.after_init.write_code( @@ -238,7 +242,7 @@ def _generate_iter_methods(self, code_sink): static PyObject* %(CONTAINER_ITER_FUNC)s(%(PYSTRUCT)s *self) { - %(ITER_PYSTRUCT)s *iter = PyObject_GC_New(%(ITER_PYSTRUCT)s, &%(ITER_PYTYPESTRUCT)s); + %(ITER_PYSTRUCT)s *iter = PyObject_GC_New(%(ITER_PYSTRUCT)s, _TYPEREF %(ITER_PYTYPESTRUCT)s); Py_INCREF(self); iter->container = self; iter->iterator = new %(CTYPE)s::%(ITERATOR_TYPE)s(self->obj->%(BEGIN_METHOD)s()); diff --git a/pybindgen/enum.py b/pybindgen/enum.py index 960da58..461ffa0 100644 --- a/pybindgen/enum.py +++ b/pybindgen/enum.py @@ -169,10 +169,15 @@ def generate(self, unused_code_sink): module.after_init.write_code( ' // %s\n' 'tmp_value = PyLong_FromLong(%s);\n' + '#ifdef Py_LIMITED_API\n' + 'PyObject_SetAttrString((PyObject*) %s, (char *) \"%s\", tmp_value);\n' + '#else\n' 'PyDict_SetItemString((PyObject*) %s.tp_dict, \"%s\", tmp_value);\n' + '#endif\n' 'Py_DECREF(tmp_value);' % ( - value_str, value_str, self.outer_class.pytypestruct, value_name)) + value_str, value_str, self.outer_class.pytypestruct, value_name, + self.outer_class.pytypestruct, value_name)) module.after_init.unindent() module.after_init.write_code("}") diff --git a/pybindgen/overloading.py b/pybindgen/overloading.py index f5b93c4..d307a84 100644 --- a/pybindgen/overloading.py +++ b/pybindgen/overloading.py @@ -238,7 +238,7 @@ def generate(self, code_sink): code_sink.writeln('error_list = PyList_New(%i);' % len(delegate_wrappers)) for i in range(len(delegate_wrappers)): code_sink.writeln( - 'PyList_SET_ITEM(error_list, %i, PyObject_Str(exceptions[%i]));' + 'PyList_SetItem(error_list, %i, PyObject_Str(exceptions[%i]));' % (i, i)) code_sink.writeln("Py_DECREF(exceptions[%i]);" % i) code_sink.writeln('PyErr_SetObject(PyExc_TypeError, error_list);') diff --git a/pybindgen/pytypeobject.py b/pybindgen/pytypeobject.py index 7dc6d51..d82f8ca 100644 --- a/pybindgen/pytypeobject.py +++ b/pybindgen/pytypeobject.py @@ -2,7 +2,31 @@ The class PyTypeObject generates a PyTypeObject structure contents. """ + class PyTypeObject(object): + LIMITED_API_TEMPLATE = ( + 'static PyType_Slot %(typestruct)s_slots[] = {\n' + ' {Py_tp_dealloc, (void*)%(tp_dealloc)s},\n' + ' {Py_tp_richcompare, (void*)%(tp_richcompare)s},\n' + ' {Py_tp_traverse, (void*)%(tp_traverse)s},\n' + ' {Py_tp_clear, (void*)%(tp_clear)s},\n' + ' {Py_tp_iter, (void*)%(tp_iter)s},\n' + ' {Py_tp_iternext, (void*)%(tp_iternext)s},\n' + ' {Py_tp_methods, (struct PyMethodDef*)%(tp_methods)s},\n' + ' {Py_tp_init, (void*)%(tp_init)s},\n' + ' {Py_tp_alloc, (void*)PyType_GenericAlloc},\n' + ' {Py_tp_new, (void*)PyType_GenericNew},\n' + ' {0, 0}\n' + '};\n' + 'static PyType_Spec %(typestruct)s_spec = {\n' + ' (char *) "%(tp_name)s",\n' + ' %(tp_basicsize)s,\n' + ' 0,\n' + ' %(tp_flags)s,\n' + ' %(typestruct)s_slots,\n' + '};\n\n' + 'PyTypeObject *%(typestruct)s;\n' + ) TEMPLATE = ( 'PyTypeObject %(typestruct)s = {\n' ' PyVarObject_HEAD_INIT(NULL, 0)\n' @@ -103,7 +127,11 @@ def generate(self, code_sink): slots.setdefault('tp_free', '0') slots.setdefault('tp_is_gc', 'NULL') + code_sink.writeln('#ifdef Py_LIMITED_API') + code_sink.writeln(self.LIMITED_API_TEMPLATE % slots) + code_sink.writeln('#else') code_sink.writeln(self.TEMPLATE % slots) + code_sink.writeln('#endif') class PyNumberMethods(object): diff --git a/pybindgen/utils.py b/pybindgen/utils.py index 519a58a..74c7cf9 100644 --- a/pybindgen/utils.py +++ b/pybindgen/utils.py @@ -110,8 +110,26 @@ def write_preamble(code_sink, min_python_version=None): ''') code_sink.writeln(r''' +#ifdef Py_LIMITED_API +# define _TYPEDEC * +#else +# define _TYPEDEC +#endif + +#ifdef Py_LIMITED_API +# define _TYPEREF +#else +# define _TYPEREF & +#endif + +#ifdef Py_LIMITED_API +# define PBG_SETATTR(_type, _name, _value) PyObject_SetAttrString((PyObject*) _type, (char *) _name, _value); +#else +# define PBG_SETATTR(_type, _name, _value) PyDict_SetItemString((PyObject*) _type.tp_dict, _name, _value); +#endif + #if PY_VERSION_HEX >= 0x03000000 -#if PY_VERSION_HEX >= 0x03050000 +#if PY_VERSION_HEX >= 0x03050000 && !defined(Py_LIMITED_API) typedef PyAsyncMethods* cmpfunc; #else typedef void* cmpfunc; From c4246a96b3abb947bfa45a13cdc04db33376eb14 Mon Sep 17 00:00:00 2001 From: Samuli Tuomola Date: Fri, 25 May 2018 22:07:47 +0300 Subject: [PATCH 2/3] Fallback to iterative copy of a container with limited API --- pybindgen/container.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pybindgen/container.py b/pybindgen/container.py index 785b99f..0a410d9 100644 --- a/pybindgen/container.py +++ b/pybindgen/container.py @@ -549,9 +549,12 @@ def _generate_container_constructor(self, code_sink): code_sink.writeln(r''' int %(CONTAINER_CONVERTER_FUNC_NAME)s(PyObject *arg, %(CTYPE)s *container) { + #ifndef Py_LIMITED_API if (PyObject_IsInstance(arg, (PyObject*) &%(PYTYPESTRUCT)s)) { *container = *((%(PYSTRUCT)s*)arg)->obj; - } else if (PyList_Check(arg)) { + } else + #endif + if (PyList_Check(arg)) { container->clear(); Py_ssize_t size = PyList_Size(arg); for (Py_ssize_t i = 0; i < size; i++) { @@ -581,9 +584,12 @@ def _generate_container_constructor(self, code_sink): code_sink.writeln(r''' int %(CONTAINER_CONVERTER_FUNC_NAME)s(PyObject *arg, %(CTYPE)s *container) { + #ifndef Py_LIMITED_API if (PyObject_IsInstance(arg, (PyObject*) &%(PYTYPESTRUCT)s)) { *container = *((%(PYSTRUCT)s*)arg)->obj; - } else if (PyList_Check(arg)) { + } else + #endif + if (PyList_Check(arg)) { container->clear(); Py_ssize_t size = PyList_Size(arg); for (Py_ssize_t i = 0; i < size; i++) { From fbc2ea9552f554dba008ede1117255cab4394e22 Mon Sep 17 00:00:00 2001 From: Samuli Tuomola Date: Sat, 26 May 2018 19:49:11 +0300 Subject: [PATCH 3/3] Cut down on ifdefs (PBG_SETATTR) --- pybindgen/cppclass.py | 8 ++------ pybindgen/enum.py | 9 ++------- pybindgen/utils.py | 4 ++-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 7cb1211..7a4136b 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -2004,12 +2004,8 @@ def generate(self, code_sink, module): 'PyModule_AddObject(m, (char *) \"%s\", (PyObject *) _TYPEREF %s);' % ( class_python_name, self.pytypestruct)) else: - module.after_init.write_code( - '#ifdef Py_LIMITED_API\n' - 'PyObject_SetAttrString((PyObject*) %s, (char *) \"%s\", (PyObject *) %s);\n' - '#else\n' - 'PyDict_SetItemString((PyObject*) %s.tp_dict, \"%s\", (PyObject *) &%s);\n' - '#endif\n' % (self.outer_class.pytypestruct, class_python_name, self.pytypestruct, + module.after_init.write_code('PBG_SETATTR(%s, \"%s\", _TYPEREF %s);\n' + % ( self.outer_class.pytypestruct, class_python_name, self.pytypestruct)) have_constructor = self._generate_constructor(code_sink) diff --git a/pybindgen/enum.py b/pybindgen/enum.py index 461ffa0..7779887 100644 --- a/pybindgen/enum.py +++ b/pybindgen/enum.py @@ -169,15 +169,10 @@ def generate(self, unused_code_sink): module.after_init.write_code( ' // %s\n' 'tmp_value = PyLong_FromLong(%s);\n' - '#ifdef Py_LIMITED_API\n' - 'PyObject_SetAttrString((PyObject*) %s, (char *) \"%s\", tmp_value);\n' - '#else\n' - 'PyDict_SetItemString((PyObject*) %s.tp_dict, \"%s\", tmp_value);\n' - '#endif\n' + 'PBG_SETATTR(%s, \"%s\", tmp_value);\n' 'Py_DECREF(tmp_value);' % ( - value_str, value_str, self.outer_class.pytypestruct, value_name, - self.outer_class.pytypestruct, value_name)) + value_str, value_str, self.outer_class.pytypestruct, value_name)) module.after_init.unindent() module.after_init.write_code("}") diff --git a/pybindgen/utils.py b/pybindgen/utils.py index 74c7cf9..2b30543 100644 --- a/pybindgen/utils.py +++ b/pybindgen/utils.py @@ -123,9 +123,9 @@ def write_preamble(code_sink, min_python_version=None): #endif #ifdef Py_LIMITED_API -# define PBG_SETATTR(_type, _name, _value) PyObject_SetAttrString((PyObject*) _type, (char *) _name, _value); +# define PBG_SETATTR(_type, _name, _value) PyObject_SetAttrString((PyObject*) _type, (char *) _name, (PyObject*) _value); #else -# define PBG_SETATTR(_type, _name, _value) PyDict_SetItemString((PyObject*) _type.tp_dict, _name, _value); +# define PBG_SETATTR(_type, _name, _value) PyDict_SetItemString((PyObject*) _type.tp_dict, _name, (PyObject*) _value); #endif #if PY_VERSION_HEX >= 0x03000000