[pypy-svn] r74266 - in pypy/branch/py-packagecontext/pypy/module/cpyext: . test

exarkun at codespeak.net exarkun at codespeak.net
Fri Apr 30 05:21:13 CEST 2010


Author: exarkun
Date: Fri Apr 30 05:21:12 2010
New Revision: 74266

Added:
   pypy/branch/py-packagecontext/pypy/module/cpyext/test/date.c
Modified:
   pypy/branch/py-packagecontext/pypy/module/cpyext/api.py
   pypy/branch/py-packagecontext/pypy/module/cpyext/modsupport.py
   pypy/branch/py-packagecontext/pypy/module/cpyext/state.py
   pypy/branch/py-packagecontext/pypy/module/cpyext/test/test_cpyext.py
Log:
Add a test for recursive extension module imports; and probably fix both of these new tests

Modified: pypy/branch/py-packagecontext/pypy/module/cpyext/api.py
==============================================================================
--- pypy/branch/py-packagecontext/pypy/module/cpyext/api.py	(original)
+++ pypy/branch/py-packagecontext/pypy/module/cpyext/api.py	Fri Apr 30 05:21:12 2010
@@ -745,24 +745,28 @@
 @unwrap_spec(ObjSpace, str, str)
 def load_extension_module(space, path, name):
     state = space.fromcache(State)
-    from pypy.rlib import libffi
+    state.package_context = name
     try:
-        dll = libffi.CDLL(path, False)
-    except libffi.DLOpenError, e:
-        raise operationerrfmt(
-            space.w_ImportError,
-            "unable to load extension module '%s': %s",
-            path, e.msg)
-    try:
-        initptr = libffi.dlsym(dll.lib, 'init%s' % (name.split('.')[-1],))
-    except KeyError:
-        raise operationerrfmt(
-            space.w_ImportError,
-            "function init%s not found in library %s",
-            name, path)
-    initfunc = rffi.cast(initfunctype, initptr)
-    generic_cpy_call(space, initfunc)
-    state.check_and_raise_exception()
+        from pypy.rlib import libffi
+        try:
+            dll = libffi.CDLL(path, False)
+        except libffi.DLOpenError, e:
+            raise operationerrfmt(
+                space.w_ImportError,
+                "unable to load extension module '%s': %s",
+                path, e.msg)
+        try:
+            initptr = libffi.dlsym(dll.lib, 'init%s' % (name.split('.')[-1],))
+        except KeyError:
+            raise operationerrfmt(
+                space.w_ImportError,
+                "function init%s not found in library %s",
+                name, path)
+        initfunc = rffi.cast(initfunctype, initptr)
+        generic_cpy_call(space, initfunc)
+        state.check_and_raise_exception()
+    finally:
+        state.package_context = None
 
 @specialize.ll()
 def generic_cpy_call(space, func, *args):

Modified: pypy/branch/py-packagecontext/pypy/module/cpyext/modsupport.py
==============================================================================
--- pypy/branch/py-packagecontext/pypy/module/cpyext/modsupport.py	(original)
+++ pypy/branch/py-packagecontext/pypy/module/cpyext/modsupport.py	Fri Apr 30 05:21:12 2010
@@ -5,14 +5,18 @@
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, PyMethodDef, PyCFunction
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
+from pypy.module.cpyext.state import State
 from pypy.interpreter.error import OperationError
 
 def PyImport_AddModule(space, name):
     w_name = space.wrap(name)
-    w_mod = space.wrap(Module(space, w_name))
-
     w_modules = space.sys.get('modules')
-    space.setitem(w_modules, w_name, w_mod)
+
+    w_mod = space.finditem_str(w_modules, name)
+    if w_mod is None:
+        w_mod = space.wrap(Module(space, w_name))
+        space.setitem(w_modules, w_name, w_mod)
+
     return w_mod
 
 @cpython_api([CONST_STRING, lltype.Ptr(PyMethodDef), CONST_STRING,
@@ -20,7 +24,9 @@
 def Py_InitModule4(space, name, methods, doc, w_self, apiver):
     from pypy.module.cpyext.typeobjectdefs import PyTypeObjectPtr
     modname = rffi.charp2str(name)
-    w_mod = PyImport_AddModule(space, modname)
+    state = space.fromcache(State)
+    w_mod = PyImport_AddModule(space, state.package_context)
+
     dict_w = {}
     convert_method_defs(space, dict_w, methods, lltype.nullptr(PyTypeObjectPtr.TO), w_self)
     for key, w_value in dict_w.items():

Modified: pypy/branch/py-packagecontext/pypy/module/cpyext/state.py
==============================================================================
--- pypy/branch/py-packagecontext/pypy/module/cpyext/state.py	(original)
+++ pypy/branch/py-packagecontext/pypy/module/cpyext/state.py	Fri Apr 30 05:21:12 2010
@@ -22,6 +22,17 @@
         self.exc_value = None
         self.new_method_def = lltype.nullptr(PyMethodDef)
 
+        # When importing a package, use this to keep track of its name.  This is
+        # necessary because an extension module in a package might not supply
+        # its own fully qualified name to Py_InitModule.  If it doesn't, we need
+        # to be able to figure out what module is being initialized.  When a
+        # package import is in progress, this is set to the name of the package.
+        # The rest of the time, it's None.  Packages may be imported
+        # recursively, in which case the outer state is preserved somewhere in
+        # the stack and then restored when the inner import is complete.
+        self.package_context = None
+
+
     def _freeze_(self):
         assert not self.borrowed_objects and not self.borrow_mapping
         self.py_objects_r2w.clear() # is not valid anymore after translation

Added: pypy/branch/py-packagecontext/pypy/module/cpyext/test/date.c
==============================================================================
--- (empty file)
+++ pypy/branch/py-packagecontext/pypy/module/cpyext/test/date.c	Fri Apr 30 05:21:12 2010
@@ -0,0 +1,11 @@
+#include "Python.h"
+
+static PyMethodDef date_functions[] = {
+    {NULL, NULL}
+};
+
+void initdate(void)
+{
+    Py_InitModule("date", date_functions);
+    PyImport_ImportModule("apple.banana");
+}

Modified: pypy/branch/py-packagecontext/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/branch/py-packagecontext/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/branch/py-packagecontext/pypy/module/cpyext/test/test_cpyext.py	Fri Apr 30 05:21:12 2010
@@ -10,7 +10,7 @@
 from pypy.translator import platform
 from pypy.translator.gensupp import uniquemodulename
 from pypy.tool.udir import udir
-from pypy.module.cpyext import api
+from pypy.module.cpyext import api, typeobject
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.pyobject import Py_DecRef, InvalidPointerException
 from pypy.translator.goal import autopath
@@ -50,8 +50,9 @@
         raises(ImportError, cpyext.load_module, self.libc, "invalid.function")
 
 def compile_module(modname, **kwds):
+    modname = modname.split('.')[-1]
     eci = ExternalCompilationInfo(
-        export_symbols=['init%s' % (modname.split('.')[-1],)],
+        export_symbols=['init%s' % (modname,)],
         include_dirs=api.include_dirs,
         **kwds
         )
@@ -97,7 +98,7 @@
             if delta != 0:
                 leaking = True
                 print >>sys.stderr, "Leaking %r: %i references" % (w_obj, delta)
-                lifeline = api.lifeline_dict.get(w_obj)
+                lifeline = typeobject.lifeline_dict.get(w_obj)
                 if lifeline is not None:
                     refcnt = lifeline.pyo.c_ob_refcnt
                     if refcnt > 0:
@@ -133,6 +134,19 @@
         cls.space = gettestobjspace(usemodules=['cpyext', 'thread'])
         cls.space.getbuiltinmodule("cpyext")
 
+    def compile_module(self, name, **kwds):
+        state = self.space.fromcache(State)
+        api_library = state.api_lib
+        if sys.platform == 'win32':
+            kwds["libraries"] = [api_library]
+            # '%s' undefined; assuming extern returning int
+            kwds["compile_extra"] = ["/we4013"]
+        else:
+            kwds["link_files"] = [str(api_library + '.so')]
+            kwds["compile_extra"] = ["-Werror=implicit-function-declaration"]
+        return compile_module(name, **kwds)
+
+
     def import_module(self, name, init=None, body='', load_it=True, filename=None):
         """
         init specifies the overall template of the module.
@@ -160,20 +174,11 @@
                     / 'cpyext'/ 'test' / (filename + ".c")
             kwds = dict(separate_module_files=[filename])
 
-        state = self.space.fromcache(State)
-        api_library = state.api_lib
-        if sys.platform == 'win32':
-            kwds["libraries"] = [api_library]
-            # '%s' undefined; assuming extern returning int
-            kwds["compile_extra"] = ["/we4013"]
-        else:
-            kwds["link_files"] = [str(api_library + '.so')]
-            kwds["compile_extra"] = ["-Werror=implicit-function-declaration"]
-        mod = compile_module(name, **kwds)
+        mod = self.compile_module(name, **kwds)
 
-        self.name = name
         if load_it:
             api.load_extension_module(self.space, mod, name)
+            self.imported_module_names.append(name)
             return self.space.getitem(
                 self.space.sys.get('modules'),
                 self.space.wrap(name))
@@ -204,9 +209,21 @@
         init = """Py_InitModule("%s", methods);""" % (modname,)
         return self.import_module(name=modname, init=init, body=body)
 
+    def record_imported_module(self, name):
+        self.imported_module_names.append(name)
+
     def setup_method(self, func):
+        # A list of modules which the test caused to be imported (in
+        # self.space).  These will be cleaned up automatically in teardown.
+        self.imported_module_names = []
         self.w_import_module = self.space.wrap(self.import_module)
         self.w_import_extension = self.space.wrap(self.import_extension)
+        self.w_compile_module = self.space.wrap(self.compile_module)
+        self.w_record_imported_module = self.space.wrap(
+            self.record_imported_module)
+        self.w_here = self.space.wrap(
+            str(py.path.local(autopath.pypydir)) + '/module/cpyext/test/')
+
 
         # create the file lock before we count allocations
         self.space.call_method(self.space.sys.get("stdout"), "flush")
@@ -214,13 +231,17 @@
         freeze_refcnts(self)
         #self.check_and_print_leaks()
 
+    def unimport_module(self, name):
+        w_modules = self.space.sys.get('modules')
+        w_name = self.space.wrap(name)
+        w_mod = self.space.getitem(w_modules, w_name)
+        self.space.delitem(w_modules, w_name)
+        Py_DecRef(self.space, w_mod)
+
     def teardown_method(self, func):
         try:
-            w_mod = self.space.getitem(self.space.sys.get('modules'),
-                               self.space.wrap(self.name))
-            self.space.delitem(self.space.sys.get('modules'),
-                               self.space.wrap(self.name))
-            Py_DecRef(self.space, w_mod)
+            for name in self.imported_module_names:
+                self.unimport_module(name)
             state = self.space.fromcache(State)
             for w_obj in state.non_heaptypes:
                 Py_DecRef(self.space, w_obj)
@@ -339,11 +360,39 @@
         only "banana" as a name, the resulting module nevertheless is stored at
         `sys.modules["apple.banana"]`.
         """
-        skip("About to implement this")
         module = self.import_module(name="apple.banana", filename="banana")
         assert module.__name__ == "apple.banana"
 
 
+    def test_recursivePackageImport(self):
+        """
+        If `cherry.date` is an extension module which imports `apple.banana`,
+        the latter is added to `sys.modules` for the `"apple.banana"` key.
+        """
+        # Build the extensions.
+        banana = self.compile_module(
+            "apple.banana", separate_module_files=[self.here + 'banana.c'])
+        self.record_imported_module("apple.banana")
+        date = self.compile_module(
+            "cherry.date", separate_module_files=[self.here + 'date.c'])
+        self.record_imported_module("cherry.date")
+
+        # Set up some package state so that the extensions can actually be
+        # imported.
+        import sys, types, os
+        cherry = sys.modules['cherry'] = types.ModuleType('cherry')
+        cherry.__path__ = [os.path.dirname(date)]
+
+        apple = sys.modules['apple'] = types.ModuleType('apple')
+        apple.__path__ = [os.path.dirname(banana)]
+
+        import cherry.date
+        import apple.banana
+
+        assert sys.modules['apple.banana'].__name__ == 'apple.banana'
+        assert sys.modules['cherry.date'].__name__ == 'cherry.date'
+
+
     def test_modinit_func(self):
         """
         A module can use the PyMODINIT_FUNC macro to declare or define its



More information about the Pypy-commit mailing list