[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