Commit 45a17f1a authored by Stefan Behnel's avatar Stefan Behnel

revert actual tp_finalize() usages, needs more thought

parent abe914c4
...@@ -8,10 +8,6 @@ Cython Changelog ...@@ -8,10 +8,6 @@ Cython Changelog
Features added Features added
-------------- --------------
* Starting with CPython 3.4, the user provided finalisation code in the
``__dealloc__()`` special method is called by ``tp_finalize()`` instead
of ``tp_dealloc()`` to provide a safer execution environment.
* During cyclic garbage collection, attributes of extension types that * During cyclic garbage collection, attributes of extension types that
cannot create reference cycles due to their type (e.g. strings) are cannot create reference cycles due to their type (e.g. strings) are
no longer considered for traversal or clearing. no longer considered for traversal or clearing.
......
...@@ -1006,7 +1006,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1006,7 +1006,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if scope: # could be None if there was an error if scope: # could be None if there was an error
self.generate_exttype_vtable(scope, code) self.generate_exttype_vtable(scope, code)
self.generate_new_function(scope, code, entry) self.generate_new_function(scope, code, entry)
self.generate_finalize_function(scope, code)
self.generate_dealloc_function(scope, code) self.generate_dealloc_function(scope, code)
if scope.needs_gc(): if scope.needs_gc():
self.generate_traverse_function(scope, code, entry) self.generate_traverse_function(scope, code, entry)
...@@ -1175,37 +1174,30 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1175,37 +1174,30 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if tp_slot.slot_code(scope) != slot_func: if tp_slot.slot_code(scope) != slot_func:
return # never used return # never used
cpp_class_attrs = [entry for entry in scope.var_entries
if entry.type.is_cpp_class]
needs_finalisation = scope.needs_finalisation()
needs_gc = scope.needs_gc()
slot_func_cname = scope.mangle_internal("tp_dealloc") slot_func_cname = scope.mangle_internal("tp_dealloc")
code.putln("") code.putln("")
code.putln( code.putln(
"static void %s(PyObject *o) {" % slot_func_cname) "static void %s(PyObject *o) {" % slot_func_cname)
if cpp_class_attrs:
weakref_slot = scope.lookup_here("__weakref__")
_, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries()
cpp_class_attrs = [entry for entry in scope.var_entries if entry.type.is_cpp_class]
if (py_attrs
or cpp_class_attrs
or memoryview_slices
or weakref_slot in scope.var_entries):
self.generate_self_cast(scope, code) self.generate_self_cast(scope, code)
if needs_finalisation: # We must mark ths object as (gc) untracked while tearing it down, lest
# before Py3.4, tp_finalize() isn't available, so this is # the garbage collection is invoked while running this destructor.
# the earliest possible time where we can call it ourselves if scope.needs_gc():
code.putln("#if PY_VERSION_HEX < 0x030400a1")
if needs_gc:
# We must mark ths object as (gc) untracked while tearing
# it down, lest the garbage collection is invoked while
# running this destructor.
code.putln("PyObject_GC_UnTrack(o);") code.putln("PyObject_GC_UnTrack(o);")
slot_func_cname = scope.mangle_internal("tp_finalize")
code.putln("%s(o);" % slot_func_cname)
if needs_gc and base_type:
# The base class deallocator probably expects this to be
# tracked, so undo the untracking above.
code.putln("PyObject_GC_Track(o);")
code.putln("#endif")
if needs_gc and not base_type: # call the user's __dealloc__
code.putln("PyObject_GC_UnTrack(o);") self.generate_usr_dealloc_call(scope, code)
if weakref_slot in scope.var_entries:
code.putln("if (p->__weakref__) PyObject_ClearWeakRefs(o);")
for entry in cpp_class_attrs: for entry in cpp_class_attrs:
split_cname = entry.type.cname.split('::') split_cname = entry.type.cname.split('::')
...@@ -1214,10 +1206,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1214,10 +1206,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
while destructor_name.count('<') != destructor_name.count('>'): while destructor_name.count('<') != destructor_name.count('>'):
destructor_name = split_cname.pop() + '::' + destructor_name destructor_name = split_cname.pop() + '::' + destructor_name
destructor_name = destructor_name.split('<',1)[0] destructor_name = destructor_name.split('<',1)[0]
code.putln("p->%s.%s::~%s();" % ( code.putln("p->%s.%s::~%s();" %
entry.cname, entry.type.declaration_code(""), destructor_name)) (entry.cname, entry.type.declaration_code(""), destructor_name))
for entry in py_attrs:
code.put_xdecref_clear("p->%s" % entry.cname, entry.type, nanny=False,
clear_before_decref=True)
for entry in memoryview_slices:
code.put_xdecref_memoryviewslice("p->%s" % entry.cname,
have_gil=True)
if base_type: if base_type:
# The base class deallocator probably expects this to be tracked, so
# undo the untracking above.
if scope.needs_gc():
code.putln("PyObject_GC_Track(o);")
tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot) tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_dealloc is not None: if tp_dealloc is not None:
code.putln("%s(o);" % tp_dealloc) code.putln("%s(o);" % tp_dealloc)
...@@ -1229,8 +1234,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1229,8 +1234,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# the module cleanup, which may already have cleared it. # the module cleanup, which may already have cleared it.
# In that case, fall back to traversing the type hierarchy. # In that case, fall back to traversing the type hierarchy.
base_cname = base_type.typeptr_cname base_cname = base_type.typeptr_cname
code.putln("if (likely(%s)) %s->tp_dealloc(o); " code.putln("if (likely(%s)) %s->tp_dealloc(o); else __Pyx_call_next_tp_dealloc(o, %s);" % (
"else __Pyx_call_next_tp_dealloc(o, %s);" % (
base_cname, base_cname, slot_func_cname)) base_cname, base_cname, slot_func_cname))
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c")) UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c"))
...@@ -1252,81 +1256,28 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1252,81 +1256,28 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln( code.putln(
"}") "}")
def generate_finalize_function(self, scope, code): def generate_usr_dealloc_call(self, scope, code):
if not scope.needs_finalisation():
return
weakref_slot = scope.lookup_here("__weakref__")
if weakref_slot not in scope.var_entries:
weakref_slot = None
_, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries()
slot_func_cname = scope.mangle_internal("tp_finalize")
code.putln("")
code.putln("static void %s(PyObject *o) {" % slot_func_cname)
if py_attrs or memoryview_slices or weakref_slot:
self.generate_self_cast(scope, code)
entry = scope.lookup_here("__dealloc__") entry = scope.lookup_here("__dealloc__")
if entry: if entry:
code.putln("PyObject *etype, *eval, *etb;") code.putln(
code.putln("PyErr_Fetch(&etype, &eval, &etb);") "{")
code.putln(
code.putln("#if PY_VERSION_HEX < 0x030400a1") "PyObject *etype, *eval, *etb;")
code.putln("++Py_REFCNT(o);") code.putln(
code.putln("#endif") "PyErr_Fetch(&etype, &eval, &etb);")
code.putln(
code.putln("%s(o);" % entry.func_cname) "++Py_REFCNT(o);")
code.putln("if (PyErr_Occurred()) PyErr_WriteUnraisable(o);") code.putln(
"%s(o);" %
code.putln("#if PY_VERSION_HEX < 0x030400a1") entry.func_cname)
code.putln("--Py_REFCNT(o);") code.putln(
code.putln("#endif") "if (PyErr_Occurred()) PyErr_WriteUnraisable(o);")
code.putln(
code.putln("PyErr_Restore(etype, eval, etb);") "--Py_REFCNT(o);")
code.putln(
if weakref_slot: "PyErr_Restore(etype, eval, etb);")
# Not using a preprocessor test here to avoid warning about code.putln(
# "unused variable p". Py3.4+ already cleared the weak refs "}")
# when we get here.
# FIXME: so did Py<3.4 I think? Isn't this redundant?
code.putln("if ((PY_VERSION_HEX < 0x030400a1) && p->__weakref__) "
"PyObject_ClearWeakRefs(o);")
for entry in py_attrs:
code.put_xdecref_clear("p->%s" % entry.cname, entry.type, nanny=False,
clear_before_decref=True)
for entry in memoryview_slices:
code.put_xdecref_memoryviewslice("p->%s" % entry.cname,
have_gil=True)
base_type = scope.parent_type.base_type
if base_type:
code.putln("#if PY_VERSION_HEX >= 0x030400a1")
# need to call base_type method
tp_finalize = TypeSlots.get_base_slot_function(
scope, TypeSlots.FinaliserSlot())
if tp_finalize is not None:
code.putln("%s(o);" % tp_finalize)
elif base_type.is_builtin_type:
code.putln("if (%s->tp_finalize) %s->tp_finalize(o);" % (
base_type.typeptr_cname, base_type.typeptr_cname))
else:
# This is an externally defined type. Calling through the
# cimported base type pointer directly interacts badly with
# the module cleanup, which may already have cleared it.
# In that case, fall back to traversing the type hierarchy.
base_cname = base_type.typeptr_cname
code.putln("if (likely(%s && %s->tp_finalize)) %s->tp_finalize(o); "
"else __Pyx_call_next_tp_finalize(o, %s);" % (
base_cname, base_cname, base_cname, slot_func_cname))
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpFinalize", "ExtensionTypes.c"))
code.putln("#endif")
code.putln("}")
def generate_traverse_function(self, scope, code, cclass_entry): def generate_traverse_function(self, scope, code, cclass_entry):
tp_slot = TypeSlots.GCDependentSlot("tp_traverse") tp_slot = TypeSlots.GCDependentSlot("tp_traverse")
......
...@@ -832,6 +832,25 @@ class Scope(object): ...@@ -832,6 +832,25 @@ class Scope(object):
def add_include_file(self, filename): def add_include_file(self, filename):
self.outer_scope.add_include_file(filename) self.outer_scope.add_include_file(filename)
def get_refcounted_entries(self, include_weakref=False,
include_gc_simple=True):
py_attrs = []
py_buffers = []
memoryview_slices = []
for entry in self.var_entries:
if entry.type.is_pyobject:
if include_weakref or entry.name != "__weakref__":
if include_gc_simple or not entry.type.is_gc_simple:
py_attrs.append(entry)
elif entry.type == PyrexTypes.c_py_buffer_type:
py_buffers.append(entry)
elif entry.type.is_memoryviewslice:
memoryview_slices.append(entry)
have_entries = py_attrs or py_buffers or memoryview_slices
return have_entries, (py_attrs, py_buffers, memoryview_slices)
class PreImportScope(Scope): class PreImportScope(Scope):
...@@ -1785,33 +1804,6 @@ class CClassScope(ClassScope): ...@@ -1785,33 +1804,6 @@ class CClassScope(ClassScope):
return not self.parent_type.is_gc_simple return not self.parent_type.is_gc_simple
return False return False
def get_refcounted_entries(self, include_weakref=False,
include_gc_simple=True):
py_attrs = []
py_buffers = []
memoryview_slices = []
for entry in self.var_entries:
if entry.type.is_pyobject:
if include_weakref or entry.name != "__weakref__":
if include_gc_simple or not entry.type.is_gc_simple:
py_attrs.append(entry)
elif entry.type == PyrexTypes.c_py_buffer_type:
py_buffers.append(entry)
elif entry.type.is_memoryviewslice:
memoryview_slices.append(entry)
have_entries = py_attrs or py_buffers or memoryview_slices
return have_entries, (py_attrs, py_buffers, memoryview_slices)
def needs_finalisation(self):
if self.lookup_here("__dealloc__"):
return True
has_gc_entries, _ = self.get_refcounted_entries(include_weakref=True)
if has_gc_entries:
return True
return False
def declare_var(self, name, type, pos, def declare_var(self, name, type, pos,
cname = None, visibility = 'private', cname = None, visibility = 'private',
api = 0, in_pxd = 0, is_cdef = 0): api = 0, in_pxd = 0, is_cdef = 0):
...@@ -1873,6 +1865,7 @@ class CClassScope(ClassScope): ...@@ -1873,6 +1865,7 @@ class CClassScope(ClassScope):
self.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname self.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname
return entry return entry
def declare_pyfunction(self, name, pos, allow_redefine=False): def declare_pyfunction(self, name, pos, allow_redefine=False):
# Add an entry for a method. # Add an entry for a method.
if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'): if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'):
...@@ -2040,7 +2033,6 @@ class CClassScope(ClassScope): ...@@ -2040,7 +2033,6 @@ class CClassScope(ClassScope):
if base_entry.utility_code: if base_entry.utility_code:
entry.utility_code = base_entry.utility_code entry.utility_code = base_entry.utility_code
class CppClassScope(Scope): class CppClassScope(Scope):
# Namespace of a C++ class. # Namespace of a C++ class.
......
...@@ -331,21 +331,6 @@ class GCDependentSlot(InternalMethodSlot): ...@@ -331,21 +331,6 @@ class GCDependentSlot(InternalMethodSlot):
return InternalMethodSlot.slot_code(self, scope) return InternalMethodSlot.slot_code(self, scope)
class FinaliserSlot(InternalMethodSlot):
"""
Descriptor for tp_finalize().
"""
def __init__(self):
InternalMethodSlot.__init__(
self, 'tp_finalize',
ifdef="PY_VERSION_HEX >= 0x030400a1")
def slot_code(self, scope):
if not scope.needs_finalisation():
return '0'
return InternalMethodSlot.slot_code(self, scope)
class ConstructorSlot(InternalMethodSlot): class ConstructorSlot(InternalMethodSlot):
# Descriptor for tp_new and tp_dealloc. # Descriptor for tp_new and tp_dealloc.
...@@ -405,8 +390,6 @@ class TypeFlagsSlot(SlotDescriptor): ...@@ -405,8 +390,6 @@ class TypeFlagsSlot(SlotDescriptor):
value += "|Py_TPFLAGS_BASETYPE" value += "|Py_TPFLAGS_BASETYPE"
if scope.needs_gc(): if scope.needs_gc():
value += "|Py_TPFLAGS_HAVE_GC" value += "|Py_TPFLAGS_HAVE_GC"
if scope.needs_finalisation():
value += "|Py_TPFLAGS_HAVE_FINALIZE"
return value return value
...@@ -805,7 +788,8 @@ slot_table = ( ...@@ -805,7 +788,8 @@ slot_table = (
EmptySlot("tp_weaklist"), EmptySlot("tp_weaklist"),
EmptySlot("tp_del"), EmptySlot("tp_del"),
EmptySlot("tp_version_tag", ifdef="PY_VERSION_HEX >= 0x02060000"), EmptySlot("tp_version_tag", ifdef="PY_VERSION_HEX >= 0x02060000"),
FinaliserSlot(), # 'tp_finalize' # TODO: change __dealloc__ to be called by tp_finalize (PEP 442)
EmptySlot("tp_finalize", ifdef="PY_VERSION_HEX >= 0x03040a00"),
) )
#------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------
......
...@@ -634,7 +634,7 @@ static PyTypeObject __pyx_CyFunctionType_type = { ...@@ -634,7 +634,7 @@ static PyTypeObject __pyx_CyFunctionType_type = {
#if PY_VERSION_HEX >= 0x02060000 #if PY_VERSION_HEX >= 0x02060000
0, /*tp_version_tag*/ 0, /*tp_version_tag*/
#endif #endif
#if PY_VERSION_HEX >= 0x030400a1 #if PY_VERSION_HEX >= 0x03040a00
0, /*tp_finalize*/ 0, /*tp_finalize*/
#endif #endif
}; };
...@@ -1082,7 +1082,7 @@ static PyTypeObject __pyx_FusedFunctionType_type = { ...@@ -1082,7 +1082,7 @@ static PyTypeObject __pyx_FusedFunctionType_type = {
#if PY_VERSION_HEX >= 0x02060000 #if PY_VERSION_HEX >= 0x02060000
0, /*tp_version_tag*/ 0, /*tp_version_tag*/
#endif #endif
#if PY_VERSION_HEX >= 0x030400a1 #if PY_VERSION_HEX >= 0x03040a00
0, /*tp_finalize*/ 0, /*tp_finalize*/
#endif #endif
}; };
......
...@@ -16,27 +16,6 @@ static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_deal ...@@ -16,27 +16,6 @@ static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_deal
type->tp_dealloc(obj); type->tp_dealloc(obj);
} }
/////////////// CallNextTpFinalize.proto ///////////////
#if PY_VERSION_HEX >= 0x030400a1
static void __Pyx_call_next_tp_finalize(PyObject* obj, destructor current_tp_finalize);
#endif
/////////////// CallNextTpFinalize ///////////////
#if PY_VERSION_HEX >= 0x030400a1
static void __Pyx_call_next_tp_finalize(PyObject* obj, destructor current_tp_finalize) {
PyTypeObject* type = Py_TYPE(obj);
/* try to find the first parent type that has a different tp_finalize() function */
while (type && type->tp_finalize != current_tp_finalize)
type = type->tp_base;
while (type && (!type->tp_finalize || type->tp_finalize == current_tp_finalize))
type = type->tp_base;
if (type)
type->tp_finalize(obj);
}
#endif
/////////////// CallNextTpTraverse.proto /////////////// /////////////// CallNextTpTraverse.proto ///////////////
static int __Pyx_call_next_tp_traverse(PyObject* obj, visitproc v, void *a, traverseproc current_tp_traverse); static int __Pyx_call_next_tp_traverse(PyObject* obj, visitproc v, void *a, traverseproc current_tp_traverse);
......
...@@ -460,20 +460,16 @@ static void __Pyx_Generator_dealloc(PyObject *self) { ...@@ -460,20 +460,16 @@ static void __Pyx_Generator_dealloc(PyObject *self) {
PyObject_GC_UnTrack(gen); PyObject_GC_UnTrack(gen);
if (gen->gi_weakreflist != NULL) if (gen->gi_weakreflist != NULL)
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
PyObject_GC_Track(self);
if (gen->resume_label > 0) { if (gen->resume_label > 0) {
/* Generator is paused, so we need to close */ /* Generator is paused, so we need to close */
PyObject_GC_Track(self);
#if PY_VERSION_HEX >= 0x030400a1
if (PyObject_CallFinalizerFromDealloc(self))
#else
Py_TYPE(gen)->tp_del(self); Py_TYPE(gen)->tp_del(self);
if (self->ob_refcnt > 0) if (self->ob_refcnt > 0)
#endif
return; /* resurrected. :( */ return; /* resurrected. :( */
PyObject_GC_UnTrack(self);
} }
PyObject_GC_UnTrack(self);
__Pyx_Generator_clear(self); __Pyx_Generator_clear(self);
PyObject_GC_Del(gen); PyObject_GC_Del(gen);
} }
...@@ -486,11 +482,9 @@ static void __Pyx_Generator_del(PyObject *self) { ...@@ -486,11 +482,9 @@ static void __Pyx_Generator_del(PyObject *self) {
if (gen->resume_label <= 0) if (gen->resume_label <= 0)
return ; return ;
#if PY_VERSION_HEX < 0x030400a1
/* Temporarily resurrect the object. */ /* Temporarily resurrect the object. */
assert(self->ob_refcnt == 0); assert(self->ob_refcnt == 0);
self->ob_refcnt = 1; self->ob_refcnt = 1;
#endif
/* Save the current exception, if any. */ /* Save the current exception, if any. */
__Pyx_ErrFetch(&error_type, &error_value, &error_traceback); __Pyx_ErrFetch(&error_type, &error_value, &error_traceback);
...@@ -505,7 +499,6 @@ static void __Pyx_Generator_del(PyObject *self) { ...@@ -505,7 +499,6 @@ static void __Pyx_Generator_del(PyObject *self) {
/* Restore the saved exception. */ /* Restore the saved exception. */
__Pyx_ErrRestore(error_type, error_value, error_traceback); __Pyx_ErrRestore(error_type, error_value, error_traceback);
#if PY_VERSION_HEX < 0x030400a1
/* Undo the temporary resurrection; can't use DECREF here, it would /* Undo the temporary resurrection; can't use DECREF here, it would
* cause a recursive call. * cause a recursive call.
*/ */
...@@ -539,7 +532,6 @@ static void __Pyx_Generator_del(PyObject *self) { ...@@ -539,7 +532,6 @@ static void __Pyx_Generator_del(PyObject *self) {
--Py_TYPE(self)->tp_frees; --Py_TYPE(self)->tp_frees;
--Py_TYPE(self)->tp_allocs; --Py_TYPE(self)->tp_allocs;
#endif #endif
#endif
} }
static PyMemberDef __pyx_Generator_memberlist[] = { static PyMemberDef __pyx_Generator_memberlist[] = {
...@@ -586,7 +578,7 @@ static PyTypeObject __pyx_GeneratorType_type = { ...@@ -586,7 +578,7 @@ static PyTypeObject __pyx_GeneratorType_type = {
0, /*tp_getattro*/ 0, /*tp_getattro*/
0, /*tp_setattro*/ 0, /*tp_setattro*/
0, /*tp_as_buffer*/ 0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags*/
0, /*tp_doc*/ 0, /*tp_doc*/
(traverseproc) __Pyx_Generator_traverse, /*tp_traverse*/ (traverseproc) __Pyx_Generator_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
...@@ -612,16 +604,12 @@ static PyTypeObject __pyx_GeneratorType_type = { ...@@ -612,16 +604,12 @@ static PyTypeObject __pyx_GeneratorType_type = {
0, /*tp_cache*/ 0, /*tp_cache*/
0, /*tp_subclasses*/ 0, /*tp_subclasses*/
0, /*tp_weaklist*/ 0, /*tp_weaklist*/
#if PY_VERSION_HEX >= 0x030400a1
0,
#else
__Pyx_Generator_del, /*tp_del*/ __Pyx_Generator_del, /*tp_del*/
#endif
#if PY_VERSION_HEX >= 0x02060000 #if PY_VERSION_HEX >= 0x02060000
0, /*tp_version_tag*/ 0, /*tp_version_tag*/
#endif #endif
#if PY_VERSION_HEX >= 0x030400a1 #if PY_VERSION_HEX >= 0x03040a00
__Pyx_Generator_del, /*tp_finalize*/ 0, /*tp_finalize*/
#endif #endif
}; };
......
...@@ -128,10 +128,6 @@ ...@@ -128,10 +128,6 @@
#define Py_TPFLAGS_HAVE_VERSION_TAG 0 #define Py_TPFLAGS_HAVE_VERSION_TAG 0
#endif #endif
#if PY_VERSION_HEX < 0x030400a1 && !defined(Py_TPFLAGS_HAVE_FINALIZE)
#define Py_TPFLAGS_HAVE_FINALIZE 0
#endif
/* new Py3.3 unicode type (PEP 393) */ /* new Py3.3 unicode type (PEP 393) */
#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) #if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
#define CYTHON_PEP393_ENABLED 1 #define CYTHON_PEP393_ENABLED 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment