[Python-checkins] cpython: Issue #15576: Allow extension modules to be a package's __init__
brett.cannon
python-checkins at python.org
Fri Aug 10 19:48:04 CEST 2012
http://hg.python.org/cpython/rev/1db6553f3f8c
changeset: 78486:1db6553f3f8c
user: Brett Cannon <brett at python.org>
date: Fri Aug 10 13:47:54 2012 -0400
summary:
Issue #15576: Allow extension modules to be a package's __init__
module again. Also took the opportunity to stop accidentally exporting
_imp.extension_suffixes() as public.
files:
Doc/library/importlib.rst | 8 +-
Lib/imp.py | 4 +-
Lib/importlib/_bootstrap.py | 38 +-
Lib/importlib/machinery.py | 4 +-
Lib/test/test_importlib/extension/test_case_sensitivity.py | 3 +-
Lib/test/test_importlib/extension/test_finder.py | 16 +-
Lib/test/test_importlib/extension/test_loader.py | 12 +-
Lib/test/test_importlib/extension/test_path_hook.py | 6 +-
Lib/test/test_importlib/source/test_case_sensitivity.py | 6 +-
Lib/test/test_importlib/source/test_finder.py | 8 +-
Lib/test/test_importlib/source/test_path_hook.py | 2 +-
Misc/NEWS | 2 +
Python/importlib.h | 7279 +++++----
13 files changed, 3700 insertions(+), 3688 deletions(-)
diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -671,9 +671,8 @@
The *path* argument is the directory for which the finder is in charge of
searching.
- The *loader_details* argument is a variable number of 3-item tuples each
- containing a loader, file suffixes the loader recognizes, and a boolean
- representing whether the loader handles packages.
+ The *loader_details* argument is a variable number of 2-item tuples each
+ containing a loader and a sequence of file suffixes the loader recognizes.
The finder will cache the directory contents as necessary, making stat calls
for each module search to verify the cache is not outdated. Because cache
@@ -798,7 +797,8 @@
.. method:: is_package(fullname)
- Returns ``False`` as extension modules can never be packages.
+ Returns ``True`` if the file path points to a package's ``__init__``
+ module based on :attr:`EXTENSION_SUFFIXES`.
.. method:: get_code(fullname)
diff --git a/Lib/imp.py b/Lib/imp.py
--- a/Lib/imp.py
+++ b/Lib/imp.py
@@ -9,7 +9,7 @@
from _imp import (lock_held, acquire_lock, release_lock,
load_dynamic, get_frozen_object, is_frozen_package,
init_builtin, init_frozen, is_builtin, is_frozen,
- _fix_co_filename, extension_suffixes)
+ _fix_co_filename)
# Directly exposed by this module
from importlib._bootstrap import new_module
@@ -51,7 +51,7 @@
warnings.warn('imp.get_suffixes() is deprecated; use the constants '
'defined on importlib.machinery instead',
DeprecationWarning, 2)
- extensions = [(s, 'rb', C_EXTENSION) for s in extension_suffixes()]
+ extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES]
source = [(s, 'U', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES]
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -1067,6 +1067,10 @@
return None
+# Filled in by _setup().
+EXTENSION_SUFFIXES = []
+
+
class ExtensionFileLoader:
"""Loader for extension modules.
@@ -1089,6 +1093,8 @@
module = _call_with_frames_removed(_imp.load_dynamic,
fullname, self.path)
_verbose_message('extension module loaded from {!r}', self.path)
+ if self.is_package(fullname):
+ module.__path__ = [_path_split(self.path)[0]]
return module
except:
if not is_reload and fullname in sys.modules:
@@ -1097,7 +1103,12 @@
def is_package(self, fullname):
"""Return False as an extension module can never be a package."""
- return False
+ file_name = _path_split(self.path)[1]
+ for suffix in EXTENSION_SUFFIXES:
+ if file_name == '__init__' + suffix:
+ return True
+ else:
+ return False
def get_code(self, fullname):
"""Return None as an extension module cannot create a code object."""
@@ -1283,14 +1294,10 @@
"""Initialize with the path to search on and a variable number of
3-tuples containing the loader, file suffixes the loader recognizes,
and a boolean of whether the loader handles packages."""
- packages = []
- modules = []
- for loader, suffixes, supports_packages in details:
- modules.extend((suffix, loader) for suffix in suffixes)
- if supports_packages:
- packages.extend((suffix, loader) for suffix in suffixes)
- self.packages = packages
- self.modules = modules
+ loaders = []
+ for loader, suffixes in details:
+ loaders.extend((suffix, loader) for suffix in suffixes)
+ self._loaders = loaders
# Base (directory) path
self.path = path or '.'
self._path_mtime = -1
@@ -1336,7 +1343,7 @@
if cache_module in cache:
base_path = _path_join(self.path, tail_module)
if _path_isdir(base_path):
- for suffix, loader in self.packages:
+ for suffix, loader in self._loaders:
init_filename = '__init__' + suffix
full_path = _path_join(base_path, init_filename)
if _path_isfile(full_path):
@@ -1346,7 +1353,7 @@
# find a module in the next section.
is_namespace = True
# Check for a file w/ a proper suffix exists.
- for suffix, loader in self.modules:
+ for suffix, loader in self._loaders:
if cache_module + suffix in cache:
full_path = _path_join(self.path, tail_module + suffix)
if _path_isfile(full_path):
@@ -1589,9 +1596,9 @@
Each item is a tuple (loader, suffixes, allow_packages).
"""
- extensions = ExtensionFileLoader, _imp.extension_suffixes(), False
- source = SourceFileLoader, SOURCE_SUFFIXES, True
- bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES, True
+ extensions = ExtensionFileLoader, _imp.extension_suffixes()
+ source = SourceFileLoader, SOURCE_SUFFIXES
+ bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES
return [extensions, source, bytecode]
@@ -1689,9 +1696,10 @@
setattr(self_module, 'path_separators', set(path_separators))
# Constants
setattr(self_module, '_relax_case', _make_relax_case())
+ EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
if builtin_os == 'nt':
SOURCE_SUFFIXES.append('.pyw')
- if '_d.pyd' in _imp.extension_suffixes():
+ if '_d.pyd' in EXTENSION_SUFFIXES:
WindowsRegistryFinder.DEBUG_BUILD = True
diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py
--- a/Lib/importlib/machinery.py
+++ b/Lib/importlib/machinery.py
@@ -3,7 +3,8 @@
import _imp
from ._bootstrap import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
- OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES)
+ OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
+ EXTENSION_SUFFIXES)
from ._bootstrap import BuiltinImporter
from ._bootstrap import FrozenImporter
from ._bootstrap import WindowsRegistryFinder
@@ -13,7 +14,6 @@
from ._bootstrap import SourcelessFileLoader
from ._bootstrap import ExtensionFileLoader
-EXTENSION_SUFFIXES = _imp.extension_suffixes()
def all_suffixes():
"""Returns a list of all recognized module suffixes for this process"""
diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py
--- a/Lib/test/test_importlib/extension/test_case_sensitivity.py
+++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py
@@ -16,8 +16,7 @@
assert good_name != bad_name
finder = _bootstrap.FileFinder(ext_util.PATH,
(_bootstrap.ExtensionFileLoader,
- imp.extension_suffixes(),
- False))
+ _bootstrap.EXTENSION_SUFFIXES))
return finder.find_module(bad_name)
def test_case_sensitive(self):
diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py
--- a/Lib/test/test_importlib/extension/test_finder.py
+++ b/Lib/test/test_importlib/extension/test_finder.py
@@ -1,8 +1,7 @@
-from importlib import _bootstrap
+from importlib import machinery
from .. import abc
from . import util
-import imp
import unittest
class FinderTests(abc.FinderTests):
@@ -10,17 +9,16 @@
"""Test the finder for extension modules."""
def find_module(self, fullname):
- importer = _bootstrap.FileFinder(util.PATH,
- (_bootstrap.ExtensionFileLoader,
- imp.extension_suffixes(),
- False))
+ importer = machinery.FileFinder(util.PATH,
+ (machinery.ExtensionFileLoader,
+ machinery.EXTENSION_SUFFIXES))
return importer.find_module(fullname)
def test_module(self):
self.assertTrue(self.find_module(util.NAME))
def test_package(self):
- # Extension modules cannot be an __init__ for a package.
+ # No extension module as an __init__ available for testing.
pass
def test_module_in_package(self):
@@ -28,7 +26,7 @@
pass
def test_package_in_package(self):
- # Extension modules cannot be an __init__ for a package.
+ # No extension module as an __init__ available for testing.
pass
def test_package_over_module(self):
@@ -38,8 +36,6 @@
def test_failure(self):
self.assertIsNone(self.find_module('asdfjkl;'))
- # XXX Raise an exception if someone tries to use the 'path' argument?
-
def test_main():
from test.support import run_unittest
diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py
--- a/Lib/test/test_importlib/extension/test_loader.py
+++ b/Lib/test/test_importlib/extension/test_loader.py
@@ -3,6 +3,7 @@
from .. import abc
from .. import util
+import os.path
import sys
import unittest
@@ -38,11 +39,11 @@
machinery.ExtensionFileLoader)
def test_package(self):
- # Extensions are not found in packages.
+ # No extension module as __init__ available for testing.
pass
def test_lacking_parent(self):
- # Extensions are not found in packages.
+ # No extension module in a package available for testing.
pass
def test_module_reuse(self):
@@ -61,6 +62,13 @@
self.load_module(name)
self.assertEqual(cm.exception.name, name)
+ def test_is_package(self):
+ self.assertFalse(self.loader.is_package(ext_util.NAME))
+ for suffix in machinery.EXTENSION_SUFFIXES:
+ path = os.path.join('some', 'path', 'pkg', '__init__' + suffix)
+ loader = machinery.ExtensionFileLoader('pkg', path)
+ self.assertTrue(loader.is_package('pkg'))
+
def test_main():
from test.support import run_unittest
diff --git a/Lib/test/test_importlib/extension/test_path_hook.py b/Lib/test/test_importlib/extension/test_path_hook.py
--- a/Lib/test/test_importlib/extension/test_path_hook.py
+++ b/Lib/test/test_importlib/extension/test_path_hook.py
@@ -1,4 +1,4 @@
-from importlib import _bootstrap
+from importlib import machinery
from . import util
import collections
@@ -14,8 +14,8 @@
# XXX Should it only work for directories containing an extension module?
def hook(self, entry):
- return _bootstrap.FileFinder.path_hook((_bootstrap.ExtensionFileLoader,
- imp.extension_suffixes(), False))(entry)
+ return machinery.FileFinder.path_hook((machinery.ExtensionFileLoader,
+ machinery.EXTENSION_SUFFIXES))(entry)
def test_success(self):
# Path hook should handle a directory where a known extension module
diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py
--- a/Lib/test/test_importlib/source/test_case_sensitivity.py
+++ b/Lib/test/test_importlib/source/test_case_sensitivity.py
@@ -23,11 +23,9 @@
def find(self, path):
finder = machinery.FileFinder(path,
(machinery.SourceFileLoader,
- machinery.SOURCE_SUFFIXES,
- True),
+ machinery.SOURCE_SUFFIXES),
(machinery.SourcelessFileLoader,
- machinery.BYTECODE_SUFFIXES,
- True))
+ machinery.BYTECODE_SUFFIXES))
return finder.find_module(self.name)
def sensitivity_test(self):
diff --git a/Lib/test/test_importlib/source/test_finder.py b/Lib/test/test_importlib/source/test_finder.py
--- a/Lib/test/test_importlib/source/test_finder.py
+++ b/Lib/test/test_importlib/source/test_finder.py
@@ -37,9 +37,9 @@
def import_(self, root, module):
loader_details = [(machinery.SourceFileLoader,
- machinery.SOURCE_SUFFIXES, True),
+ machinery.SOURCE_SUFFIXES),
(machinery.SourcelessFileLoader,
- machinery.BYTECODE_SUFFIXES, True)]
+ machinery.BYTECODE_SUFFIXES)]
finder = machinery.FileFinder(root, *loader_details)
return finder.find_module(module)
@@ -120,7 +120,7 @@
def test_empty_string_for_dir(self):
# The empty string from sys.path means to search in the cwd.
finder = machinery.FileFinder('', (machinery.SourceFileLoader,
- machinery.SOURCE_SUFFIXES, True))
+ machinery.SOURCE_SUFFIXES))
with open('mod.py', 'w') as file:
file.write("# test file for importlib")
try:
@@ -132,7 +132,7 @@
def test_invalidate_caches(self):
# invalidate_caches() should reset the mtime.
finder = machinery.FileFinder('', (machinery.SourceFileLoader,
- machinery.SOURCE_SUFFIXES, True))
+ machinery.SOURCE_SUFFIXES))
finder._path_mtime = 42
finder.invalidate_caches()
self.assertEqual(finder._path_mtime, -1)
diff --git a/Lib/test/test_importlib/source/test_path_hook.py b/Lib/test/test_importlib/source/test_path_hook.py
--- a/Lib/test/test_importlib/source/test_path_hook.py
+++ b/Lib/test/test_importlib/source/test_path_hook.py
@@ -11,7 +11,7 @@
def path_hook(self):
return machinery.FileFinder.path_hook((machinery.SourceFileLoader,
- machinery.SOURCE_SUFFIXES, True))
+ machinery.SOURCE_SUFFIXES))
def test_success(self):
with source_util.create_modules('dummy') as mapping:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -80,6 +80,8 @@
Library
-------
+- Issue #15576: Allow extension modules to act as a package's __init__ module.
+
- Issue #15502: Have importlib.invalidate_caches() work on sys.meta_path
instead of sys.path_importer_cache.
diff --git a/Python/importlib.h b/Python/importlib.h
--- a/Python/importlib.h
+++ b/Python/importlib.h
[stripped]
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list