summaryrefslogtreecommitdiff
path: root/Cython/Compiler/ModuleNode.py
diff options
context:
space:
mode:
Diffstat (limited to 'Cython/Compiler/ModuleNode.py')
-rw-r--r--Cython/Compiler/ModuleNode.py255
1 files changed, 140 insertions, 115 deletions
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index b7f68716b..bc2ff5109 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -105,8 +105,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.return_type = PyrexTypes.c_void_type
self.referenced_modules = []
self.find_referenced_modules(env, self.referenced_modules, {})
- if options.recursive:
- self.generate_dep_file(env, result)
self.sort_cdef_classes(env)
self.generate_c_code(env, options, result)
self.generate_h_code(env, options, result)
@@ -119,20 +117,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
return 1
return 0
- def generate_dep_file(self, env, result):
- modules = self.referenced_modules
- if len(modules) > 1 or env.included_files:
- dep_file = replace_suffix(result.c_file, ".dep")
- f = open(dep_file, "w")
- try:
- for module in modules:
- if module is not env:
- f.write("cimport %s\n" % module.qualified_name)
- for path in module.included_files:
- f.write("include %s\n" % path)
- finally:
- f.close()
-
def generate_h_code(self, env, options, result):
def h_entries(entries, api=0, pxd=0):
return [entry for entry in entries
@@ -316,7 +300,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else:
emit_linenums = options.emit_linenums
rootwriter = Code.CCodeWriter(emit_linenums=emit_linenums, c_line_in_traceback=options.c_line_in_traceback)
- globalstate = Code.GlobalState(rootwriter, self, emit_linenums)
+ globalstate = Code.GlobalState(rootwriter, self, emit_linenums, options.common_utility_include_dir)
globalstate.initialize_main_c_code()
h_code = globalstate['h_code']
@@ -548,6 +532,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(" #error Cython requires Python 2.4+.")
code.putln("#else")
code.globalstate["end"].putln("#endif /* Py_PYTHON_H */")
+
+ from Cython import __version__
+ code.putln('#define CYTHON_ABI "%s"' % __version__.replace('.', '_'))
code.put(UtilityCode.load_as_string("CModulePreamble", "ModuleSetupCode.c")[1])
@@ -1029,7 +1016,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_dealloc_function(scope, code)
if scope.needs_gc():
self.generate_traverse_function(scope, code, entry)
- self.generate_clear_function(scope, code, entry)
+ if scope.needs_tp_clear():
+ self.generate_clear_function(scope, code, entry)
if scope.defines_any(["__getitem__"]):
self.generate_getitem_int_function(scope, code)
if scope.defines_any(["__setitem__", "__delitem__"]):
@@ -1192,31 +1180,52 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
slot_func = scope.mangle_internal("tp_dealloc")
base_type = scope.parent_type.base_type
if tp_slot.slot_code(scope) != slot_func:
- return # never used
+ return # never used
slot_func_cname = scope.mangle_internal("tp_dealloc")
code.putln("")
code.putln(
"static void %s(PyObject *o) {" % slot_func_cname)
+ is_final_type = scope.parent_type.is_final_type
+ needs_gc = scope.needs_gc()
+
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()
- cpp_class_attrs = [entry for entry in scope.var_entries if entry.type.is_cpp_class]
+ 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):
+ if py_attrs or cpp_class_attrs or memoryview_slices or weakref_slot:
self.generate_self_cast(scope, code)
-
- # We must mark ths object as (gc) untracked while tearing it down, lest
- # the garbage collection is invoked while running this destructor.
- if scope.needs_gc():
+
+ if not is_final_type:
+ # in Py3.4+, call tp_finalize() as early as possible
+ code.putln("#if PY_VERSION_HEX >= 0x030400a1")
+ if needs_gc:
+ finalised_check = '!_PyGC_FINALIZED(o)'
+ else:
+ finalised_check = (
+ '(!PyType_IS_GC(Py_TYPE(o)) || !_PyGC_FINALIZED(o))')
+ code.putln("if (unlikely(Py_TYPE(o)->tp_finalize) && %s) {" %
+ finalised_check)
+ # if instance was resurrected by finaliser, return
+ code.putln("if (PyObject_CallFinalizerFromDealloc(o)) return;")
+ code.putln("}")
+ code.putln("#endif")
+
+ if needs_gc:
+ # We must mark this object as (gc) untracked while tearing
+ # it down, lest the garbage collection is invoked while
+ # running this destructor.
code.putln("PyObject_GC_UnTrack(o);")
# call the user's __dealloc__
self.generate_usr_dealloc_call(scope, code)
- if weakref_slot in scope.var_entries:
+
+ if weakref_slot:
code.putln("if (p->__weakref__) PyObject_ClearWeakRefs(o);")
for entry in cpp_class_attrs:
@@ -1225,9 +1234,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# Make sure the namespace delimiter was not in a template arg.
while destructor_name.count('<') != destructor_name.count('>'):
destructor_name = split_cname.pop() + '::' + destructor_name
- destructor_name = destructor_name.split('<',1)[0]
- code.putln("p->%s.%s::~%s();" %
- (entry.cname, entry.type.declaration_code(""), destructor_name))
+ destructor_name = destructor_name.split('<', 1)[0]
+ code.putln("p->%s.%s::~%s();" % (
+ 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,
@@ -1238,10 +1247,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
have_gil=True)
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);")
+ if needs_gc:
+ # The base class deallocator probably expects this to be tracked,
+ # so undo the untracking above.
+ if base_type.scope and base_type.scope.needs_gc():
+ code.putln("PyObject_GC_Track(o);")
+ else:
+ code.putln("if (PyType_IS_GC(Py_TYPE(o)->tp_base))"
+ " PyObject_GC_Track(o);")
tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_dealloc is not None:
@@ -1254,8 +1267,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# 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_dealloc(o); else __Pyx_call_next_tp_dealloc(o, %s);" % (
- base_cname, base_cname, slot_func_cname))
+ code.putln("if (likely(%s)) %s->tp_dealloc(o); "
+ "else __Pyx_call_next_tp_dealloc(o, %s);" % (
+ base_cname, base_cname, slot_func_cname))
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c"))
else:
@@ -1278,26 +1292,17 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_usr_dealloc_call(self, scope, code):
entry = scope.lookup_here("__dealloc__")
- if entry:
- code.putln(
- "{")
- code.putln(
- "PyObject *etype, *eval, *etb;")
- code.putln(
- "PyErr_Fetch(&etype, &eval, &etb);")
- code.putln(
- "++Py_REFCNT(o);")
- code.putln(
- "%s(o);" %
- entry.func_cname)
- code.putln(
- "if (PyErr_Occurred()) PyErr_WriteUnraisable(o);")
- code.putln(
- "--Py_REFCNT(o);")
- code.putln(
- "PyErr_Restore(etype, eval, etb);")
- code.putln(
- "}")
+ if not entry:
+ return
+
+ code.putln("{")
+ code.putln("PyObject *etype, *eval, *etb;")
+ code.putln("PyErr_Fetch(&etype, &eval, &etb);")
+ code.putln("++Py_REFCNT(o);")
+ code.putln("%s(o);" % entry.func_cname)
+ code.putln("--Py_REFCNT(o);")
+ code.putln("PyErr_Restore(etype, eval, etb);")
+ code.putln("}")
def generate_traverse_function(self, scope, code, cclass_entry):
tp_slot = TypeSlots.GCDependentSlot("tp_traverse")
@@ -1310,8 +1315,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"static int %s(PyObject *o, visitproc v, void *a) {"
% slot_func)
- have_entries, (py_attrs, py_buffers,
- memoryview_slices) = scope.get_refcounted_entries()
+ have_entries, (py_attrs, py_buffers, memoryview_slices) = (
+ scope.get_refcounted_entries(include_gc_simple=False))
if base_type or py_attrs:
code.putln("int e;")
@@ -1374,13 +1379,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if tp_slot.slot_code(scope) != slot_func:
return # never used
- py_attrs = []
- py_buffers = []
- for entry in scope.var_entries:
- if entry.type.is_pyobject and entry.name != "__weakref__":
- py_attrs.append(entry)
- if entry.type == PyrexTypes.c_py_buffer_type:
- py_buffers.append(entry)
+ have_entries, (py_attrs, py_buffers, memoryview_slices) = (
+ scope.get_refcounted_entries(include_gc_simple=False))
if py_attrs or py_buffers or base_type:
unused = ''
@@ -1390,9 +1390,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("")
code.putln("static int %s(%sPyObject *o) {" % (slot_func, unused))
+ if py_attrs and Options.clear_to_none:
+ code.putln("PyObject* tmp;")
+
if py_attrs or py_buffers:
self.generate_self_cast(scope, code)
- code.putln("PyObject* tmp;")
if base_type:
# want to call it explicitly if possible so inlining can be performed
@@ -1414,14 +1416,18 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpClear", "ExtensionTypes.c"))
- for entry in py_attrs:
- name = "p->%s" % entry.cname
- code.putln("tmp = ((PyObject*)%s);" % name)
- if entry.is_declared_generic:
- code.put_init_to_py_none(name, py_object_type, nanny=False)
- else:
- code.put_init_to_py_none(name, entry.type, nanny=False)
- code.putln("Py_XDECREF(tmp);")
+ if Options.clear_to_none:
+ for entry in py_attrs:
+ name = "p->%s" % entry.cname
+ code.putln("tmp = ((PyObject*)%s);" % name)
+ if entry.is_declared_generic:
+ code.put_init_to_py_none(name, py_object_type, nanny=False)
+ else:
+ code.put_init_to_py_none(name, entry.type, nanny=False)
+ code.putln("Py_XDECREF(tmp);")
+ else:
+ for entry in py_attrs:
+ code.putln("Py_CLEAR(p->%s);" % entry.cname)
for entry in py_buffers:
# Note: shouldn't this call __Pyx_ReleaseBuffer ??
@@ -1821,6 +1827,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"};")
def generate_method_table(self, env, code):
+ if env.is_c_class_scope and not env.pyfunc_entries:
+ return
code.putln("")
code.putln(
"static PyMethodDef %s[] = {" %
@@ -1993,7 +2001,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.error_goto(self.pos)))
code.putln("}")
- self.generate_module_path_setup(env, code)
+ # set up __file__ and __path__, then add the module to sys.modules
+ self.generate_module_import_setup(env, code)
if Options.cache_builtins:
code.putln("/*--- Builtin init code ---*/")
@@ -2063,34 +2072,65 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.exit_cfunc_scope()
- def generate_module_path_setup(self, env, code):
- if not env.directives['set_initial_path']:
- return
+ def generate_module_import_setup(self, env, code):
module_path = env.directives['set_initial_path']
if module_path == 'SOURCEFILE':
module_path = self.pos[0].filename
- if not module_path:
- return
- code.putln('if (__Pyx_SetAttrString(%s, "__file__", %s) < 0) %s;' % (
- env.module_cname,
- code.globalstate.get_py_string_const(
- EncodedString(decode_filename(module_path))).cname,
- code.error_goto(self.pos)))
- if env.is_package:
- # compiling a package => set __path__ as well
- temp = code.funcstate.allocate_temp(py_object_type, True)
- code.putln('%s = Py_BuildValue("[O]", %s); %s' % (
- temp,
- code.globalstate.get_py_string_const(
- EncodedString(decode_filename(os.path.dirname(module_path)))).cname,
- code.error_goto_if_null(temp, self.pos)))
- code.put_gotref(temp)
- code.putln('if (__Pyx_SetAttrString(%s, "__path__", %s) < 0) %s;' % (
+
+ if module_path:
+ code.putln('if (__Pyx_SetAttrString(%s, "__file__", %s) < 0) %s;' % (
env.module_cname,
- temp,
+ code.globalstate.get_py_string_const(
+ EncodedString(decode_filename(module_path))).cname,
code.error_goto(self.pos)))
- code.put_decref_clear(temp, py_object_type)
- code.funcstate.release_temp(temp)
+
+ if env.is_package:
+ # set __path__ to mark the module as package
+ temp = code.funcstate.allocate_temp(py_object_type, True)
+ code.putln('%s = Py_BuildValue("[O]", %s); %s' % (
+ temp,
+ code.globalstate.get_py_string_const(
+ EncodedString(decode_filename(
+ os.path.dirname(module_path)))).cname,
+ code.error_goto_if_null(temp, self.pos)))
+ code.put_gotref(temp)
+ code.putln(
+ 'if (__Pyx_SetAttrString(%s, "__path__", %s) < 0) %s;' % (
+ env.module_cname, temp, code.error_goto(self.pos)))
+ code.put_decref_clear(temp, py_object_type)
+ code.funcstate.release_temp(temp)
+
+ elif env.is_package:
+ # packages require __path__, so all we can do is try to figure
+ # out the module path at runtime by rerunning the import lookup
+ package_name, _ = self.full_module_name.rsplit('.', 1)
+ if '.' in package_name:
+ parent_name = '"%s"' % (package_name.rsplit('.', 1)[0],)
+ else:
+ parent_name = 'NULL'
+ code.globalstate.use_utility_code(UtilityCode.load(
+ "SetPackagePathFromImportLib", "ImportExport.c"))
+ code.putln(code.error_goto_if_neg(
+ '__Pyx_SetPackagePathFromImportLib(%s, %s)' % (
+ parent_name,
+ code.globalstate.get_py_string_const(
+ EncodedString(env.module_name)).cname),
+ self.pos))
+
+ # CPython may not have put us into sys.modules yet, but relative imports and reimports require it
+ fq_module_name = self.full_module_name
+ if fq_module_name.endswith('.__init__'):
+ fq_module_name = fq_module_name[:-len('.__init__')]
+ code.putln("#if PY_MAJOR_VERSION >= 3")
+ code.putln("{")
+ code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" %
+ code.error_goto_if_null("modules", self.pos))
+ code.putln('if (!PyDict_GetItemString(modules, "%s")) {' % fq_module_name)
+ code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, "%s", %s)' % (
+ fq_module_name, env.module_cname), self.pos))
+ code.putln("}")
+ code.putln("}")
+ code.putln("#endif")
def generate_module_cleanup_func(self, env, code):
if not Options.generate_cleanup_code:
@@ -2230,21 +2270,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.error_goto_if_null(env.module_dict_cname, self.pos)))
code.put_incref(env.module_dict_cname, py_object_type, nanny=False)
- # CPython may not have put us into sys.modules yet, but relative imports and reimports require it
- fq_module_name = env.qualified_name
- if fq_module_name.endswith('.__init__'):
- fq_module_name = fq_module_name[:-len('.__init__')]
- code.putln("#if PY_MAJOR_VERSION >= 3")
- code.putln("{")
- code.putln("PyObject *modules = PyImport_GetModuleDict(); %s" %
- code.error_goto_if_null("modules", self.pos))
- code.putln('if (!PyDict_GetItemString(modules, "%s")) {' % fq_module_name)
- code.putln(code.error_goto_if_neg('PyDict_SetItemString(modules, "%s", %s)' % (
- fq_module_name, env.module_cname), self.pos))
- code.putln("}")
- code.putln("}")
- code.putln("#endif")
-
code.putln(
'%s = PyImport_AddModule(__Pyx_NAMESTR(__Pyx_BUILTIN_MODULE_NAME)); %s' % (
Naming.builtins_cname,