[pypy-commit] pypy default: Write a hack to distinguish between "true built-in modules" and

arigo noreply at buildbot.pypy.org
Fri Dec 9 18:19:06 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r50328:a72429e0e0ed
Date: 2011-12-09 18:18 +0100
http://bitbucket.org/pypy/pypy/changeset/a72429e0e0ed/

Log:	Write a hack to distinguish between "true built-in modules" and
	"pseudo-extension built-in modules". The latters are the ones that
	are extension modules in CPython. We list the formers explicitly.

	True built-in modules are treated like CPython treats built-in
	modules, i.e. they always shadow any xx.py. The pseudo-extension
	built-in modules are treated like CPython treats extension modules,
	and are only found in sys.path order for the fake entry
	'.../lib_pypy/__extensions__' which we put just before 'lib_pypy'
	and the 'lib-python' entries.

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -487,6 +487,16 @@
         'parser', 'fcntl', '_codecs', 'binascii'
     ]
 
+    # These modules are treated like CPython treats built-in modules,
+    # i.e. they always shadow any xx.py.  The other modules are treated
+    # like CPython treats extension modules, and are loaded in sys.path
+    # order by the fake entry '.../lib_pypy/__extensions__'.
+    MODULES_THAT_ALWAYS_SHADOW = dict.fromkeys([
+        '__builtin__', '__pypy__', '_ast', '_codecs', '_sre', '_warnings',
+        '_weakref', 'errno', 'exceptions', 'gc', 'imp', 'marshal',
+        'posix', 'nt', 'pwd', 'signal', 'sys', 'thread', 'zipimport',
+    ], None)
+
     def make_builtins(self):
         "NOT_RPYTHON: only for initializing the space."
 
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -483,10 +483,20 @@
     # XXX Check for frozen modules?
     #     when w_path is a string
 
+    delayed_builtin = None
+    w_lib_extensions = None
+
     if w_path is None:
         # check the builtin modules
         if modulename in space.builtin_modules:
-            return FindInfo(C_BUILTIN, modulename, None)
+            delayed_builtin = FindInfo(C_BUILTIN, modulename, None)
+            # a "real builtin module xx" shadows every file "xx.py" there
+            # could possibly be; a "pseudo-extension module" does not, and
+            # is only loaded at the point in sys.path where we find
+            # '.../lib_pypy/__extensions__'.
+            if modulename in space.MODULES_THAT_ALWAYS_SHADOW:
+                return delayed_builtin
+            w_lib_extensions = space.sys.get_state(space).w_lib_extensions
         w_path = space.sys.get('path')
 
     # XXX check frozen modules?
@@ -495,6 +505,9 @@
     if w_path is not None:
         for w_pathitem in space.unpackiterable(w_path):
             # sys.path_hooks import hook
+            if (w_lib_extensions is not None and
+                    space.is_w(w_pathitem, w_lib_extensions)):
+                return delayed_builtin
             if use_loader:
                 w_loader = find_in_path_hooks(space, w_modulename, w_pathitem)
                 if w_loader:
@@ -527,7 +540,7 @@
                        # Out of file descriptors.
 
     # not found
-    return None
+    return delayed_builtin
 
 def _prepare_module(space, w_mod, filename, pkgdir):
     w = space.wrap
diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py
--- a/pypy/module/imp/test/test_import.py
+++ b/pypy/module/imp/test/test_import.py
@@ -38,6 +38,8 @@
                     test_reload = "def test():\n    raise ValueError\n",
                     infinite_reload = "import infinite_reload; reload(infinite_reload)",
                     del_sys_module = "import sys\ndel sys.modules['del_sys_module']\n",
+                    itertools = "hello_world = 42\n",
+                    gc = "should_never_be_seen = 42\n",
                     )
     root.ensure("notapackage", dir=1)    # empty, no __init__.py
     setuppkg("pkg",
@@ -147,6 +149,7 @@
 class AppTestImport:
 
     def setup_class(cls): # interpreter-level
+        cls.space = gettestobjspace(usemodules=['itertools'])
         cls.saved_modules = _setup(cls.space)
         #XXX Compile class
 
@@ -571,6 +574,47 @@
         else:
             assert False, 'should not work'
 
+    def test_shadow_builtin(self):
+        # 'import gc' is supposed to always find the built-in module;
+        # like CPython, it is a built-in module, so it shadows everything,
+        # even though there is a gc.py.
+        import sys
+        assert 'gc' not in sys.modules
+        import gc
+        assert not hasattr(gc, 'should_never_be_seen')
+        assert '(built-in)' in repr(gc)
+        del sys.modules['gc']
+
+    def test_shadow_extension_1(self):
+        # 'import itertools' is supposed to find itertools.py if there is
+        # one in sys.path.
+        import sys
+        assert 'itertools' not in sys.modules
+        import itertools
+        assert hasattr(itertools, 'hello_world')
+        assert not hasattr(itertools, 'count')
+        assert '(built-in)' not in repr(itertools)
+        del sys.modules['itertools']
+
+    def test_shadow_extension_2(self):
+        # 'import itertools' is supposed to find the built-in module even
+        # if there is also one in sys.path as long as it is *after* the
+        # special entry '.../lib_pypy/__extensions__'.  (Note that for now
+        # there is one in lib_pypy/itertools.py, which should not be seen
+        # either; hence the (built-in) test below.)
+        import sys
+        assert 'itertools' not in sys.modules
+        sys.path.append(sys.path.pop(0))
+        try:
+            import itertools
+            assert not hasattr(itertools, 'hello_world')
+            assert hasattr(itertools, 'izip')
+            assert '(built-in)' in repr(itertools)
+        finally:
+            sys.path.insert(0, sys.path.pop())
+        del sys.modules['itertools']
+
+
 class TestAbi:
     def test_abi_tag(self):
         space1 = gettestobjspace(soabi='TEST')
diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py
--- a/pypy/module/sys/__init__.py
+++ b/pypy/module/sys/__init__.py
@@ -170,3 +170,7 @@
     def get_flag(self, name):
         space = self.space
         return space.int_w(space.getattr(self.get('flags'), space.wrap(name)))
+
+    def get_state(self, space):
+        from pypy.module.sys import state
+        return state.get(space)
diff --git a/pypy/module/sys/state.py b/pypy/module/sys/state.py
--- a/pypy/module/sys/state.py
+++ b/pypy/module/sys/state.py
@@ -24,7 +24,7 @@
         # Initialize the default path
         pypydir = os.path.dirname(os.path.abspath(pypy.__file__))
         srcdir = os.path.dirname(pypydir)
-        path = getinitialpath(srcdir)
+        path = getinitialpath(self, srcdir)
         self.w_path = space.newlist([space.wrap(p) for p in path])
 
 def checkdir(path):
@@ -35,7 +35,7 @@
 
 platform = sys.platform
 
-def getinitialpath(prefix):
+def getinitialpath(state, prefix):
     from pypy.module.sys.version import CPYTHON_VERSION
     dirname = '%d.%d' % (CPYTHON_VERSION[0],
                          CPYTHON_VERSION[1])
@@ -49,6 +49,12 @@
     checkdir(lib_pypy)
 
     importlist = []
+    #
+    if state is not None:    # 'None' for testing only
+        lib_extensions = os.path.join(lib_pypy, '__extensions__')
+        state.w_lib_extensions = state.space.wrap(lib_extensions)
+        importlist.append(lib_extensions)
+    #
     importlist.append(lib_pypy)
     importlist.append(python_std_lib_modified)
     importlist.append(python_std_lib)
@@ -71,7 +77,7 @@
 @unwrap_spec(srcdir=str)
 def pypy_initial_path(space, srcdir):
     try:
-        path = getinitialpath(srcdir)
+        path = getinitialpath(get(space), srcdir)
     except OSError:
         return space.w_None
     else:
diff --git a/pypy/module/sys/test/test_initialpath.py b/pypy/module/sys/test/test_initialpath.py
--- a/pypy/module/sys/test/test_initialpath.py
+++ b/pypy/module/sys/test/test_initialpath.py
@@ -13,7 +13,7 @@
 
 def test_stdlib_in_prefix(tmpdir):
     dirs = build_hierarchy(tmpdir)
-    path = getinitialpath(str(tmpdir))
+    path = getinitialpath(None, str(tmpdir))
     # we get at least 'dirs', and maybe more (e.g. plat-linux2)
     assert path[:len(dirs)] == map(str, dirs)
 
@@ -21,7 +21,7 @@
     lib_pypy, lib_python_modified, lib_python = build_hierarchy(tmpdir)
     lib_tk_modified = lib_python_modified.join('lib-tk')
     lib_tk = lib_python.join('lib-tk')
-    path = getinitialpath(str(tmpdir))
+    path = getinitialpath(None, str(tmpdir))
     i = path.index(str(lib_tk_modified))
     j = path.index(str(lib_tk))
     assert i < j


More information about the pypy-commit mailing list