[pypy-commit] pypy default: replicate obscure CPython package loading behavior

gutworth pypy.commits at gmail.com
Sun Jan 22 15:07:36 EST 2017


Author: Benjamin Peterson <benjamin at python.org>
Branch: 
Changeset: r89693:b289308aa50b
Date: 2017-01-22 12:04 -0800
http://bitbucket.org/pypy/pypy/changeset/b289308aa50b/

Log:	replicate obscure CPython package loading behavior

	1. When searching for an __init__ file, CPython checks for
	__init__.py and __init__.py[co] but not __init__.so or __init__.pyw.

	2. CPython considers any __init__\.py[oc]? entry it can stat
	successfully to qualify a directory as a package. This means
	__init__.py need not be a regular file and may be something weird
	like /dev/null (a character device). The behavior is less strange in
	Python 3, where __init__.py are required to be real files.

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
@@ -64,13 +64,26 @@
         space.call_method(w_stderr, "write", space.wrap(message))
 
 def file_exists(path):
-    """Tests whether the given path is an existing regular file."""
+    "Test whether the given path is an existing regular file."
     return os.path.isfile(path) and case_ok(path)
 
+def path_exists(path):
+    "Test whether the given path exists."
+    return os.path.exists(path) and case_ok(path)
+
 def has_so_extension(space):
     return (space.config.objspace.usemodules.cpyext or
             space.config.objspace.usemodules._cffi_backend)
 
+def has_init_module(space, filepart):
+    "Return True if the directory filepart qualifies as a package."
+    init = os.path.join(filepart, "__init__")
+    if path_exists(init + ".py"):
+        return True
+    if space.config.objspace.lonepycfiles and path_exists(init + ".pyc"):
+        return True
+    return False
+
 def find_modtype(space, filepart):
     """Check which kind of module to import for the given filepart,
     which is a path without extension.  Returns PY_SOURCE, PY_COMPILED or
@@ -565,9 +578,7 @@
             filepart = os.path.join(path, partname)
             log_pyverbose(space, 2, "# trying %s" % (filepart,))
             if os.path.isdir(filepart) and case_ok(filepart):
-                initfile = os.path.join(filepart, '__init__')
-                modtype, _, _ = find_modtype(space, initfile)
-                if modtype in (PY_SOURCE, PY_COMPILED):
+                if has_init_module(space, filepart):
                     return FindInfo(PKG_DIRECTORY, filepart, None)
                 else:
                     msg = ("Not importing directory '%s' missing __init__.py" %
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
@@ -108,7 +108,7 @@
     # create compiled/x.py and a corresponding pyc file
     p = setuppkg("compiled", x = "x = 84")
     if conftest.option.runappdirect:
-        import marshal, stat, struct, os, imp
+        import marshal, stat, struct, imp
         code = py.code.Source(p.join("x.py").read()).compile()
         s3 = marshal.dumps(code)
         s2 = struct.pack("<i", os.stat(str(p.join("x.py")))[stat.ST_MTIME])
@@ -138,6 +138,15 @@
         pass
     p.join('x.py').rename(p.join('x.pyw'))
 
+    if hasattr(p, "mksymlinkto"):
+        p = root.join("devnullpkg")
+        p.ensure(dir=True)
+        p.join("__init__.py").mksymlinkto(os.devnull)
+
+    p = root.join("onlypyw")
+    p.ensure(dir=True)
+    p.join("__init__.pyw")
+
     return str(root)
 
 def _load_source_module(space, w_modname, w_mod, *args, **kwds):
@@ -795,6 +804,15 @@
             reload(sys)
         assert not output
 
+    def test_dir_with_only_pyw(self):
+        def imp():
+            import onlypyw
+        raises(ImportError, imp)
+
+    @pytest.mark.skipif(not hasattr(py.path.local, "mksymlinkto"), reason="requires symlinks")
+    def test_dev_null_init_file(self):
+        import devnullpkg
+
 
 class TestAbi:
     def test_abi_tag(self):


More information about the pypy-commit mailing list