diff options
Diffstat (limited to 'Cython/Compiler/ModuleNode.py')
-rw-r--r-- | Cython/Compiler/ModuleNode.py | 255 |
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, |