[Python-checkins] r52594 - sandbox/trunk/import_in_py/importer.py sandbox/trunk/import_in_py/mock_importer.py sandbox/trunk/import_in_py/test_importer.py
brett.cannon
python-checkins at python.org
Thu Nov 2 22:43:18 CET 2006
Author: brett.cannon
Date: Thu Nov 2 22:43:17 2006
New Revision: 52594
Modified:
sandbox/trunk/import_in_py/importer.py
sandbox/trunk/import_in_py/mock_importer.py
sandbox/trunk/import_in_py/test_importer.py
Log:
Add support in the Import class for handling modules contained within a
top-level package.
It is untested whether this will work for arbitrarily nested packages. Also no
support yet in filesystem importer. Still need to look at what __path__ is
used for when found in globals argument to __import__.
Modified: sandbox/trunk/import_in_py/importer.py
==============================================================================
--- sandbox/trunk/import_in_py/importer.py (original)
+++ sandbox/trunk/import_in_py/importer.py Thu Nov 2 22:43:17 2006
@@ -62,10 +62,18 @@
this means that the usual path_hooks/path_importer_cache dance is done for
entries in __path__ instead of sys.path (meta_path is still searched
regardless of the value of __path__) [PEP 302].
+* Only meta_path entries have the 'path' argument for find_module and
+ load_module set. path_importer_cache entries (which covers sys.path and
+ entries for __path__ for package) do not have 'path' set to anything (but
+ still have the full name of the module passed in)
+ [pep 302 and introspection(Python/import.c:1278)].
Implementing
------------
#. Importing module within a package.
+ + One level deep.
+ + Arbitrarily deep.
+ + Search __path__ and not sys.path.
#. Importing within package.
+ Root package already imported.
+ Root package not imported yet.
@@ -533,7 +541,7 @@
handlers = ExtensionFileHandler(), PyPycHandler()
self.default_path_hook = FileSystemFactory(*handlers)
- def search_meta_path(self, name):
+ def search_meta_path(self, name, path=None):
"""Check the importers on sys.meta_path for a loader along with the
extended meta path sequence stored within this instance.
@@ -542,17 +550,17 @@
"""
for entry in (tuple(sys.meta_path) + self.extended_meta_path):
- loader = entry.find_module(name)
+ loader = entry.find_module(name, path)
if loader:
return loader
else:
- raise ImportError("%s not found on meta path" % name)
+ raise ImportError("%s not found on sys.meta_path" % name)
def sys_path_importer(self, path_entry):
- """Return the importer for the entry on sys.path.
+ """Return the importer for the specified path.
- If an entry on sys.path has None stored in sys.path_importer_cache
- then use the default path hook.
+ If None is stored in sys.path_importer_cache then use the default path
+ hook.
"""
try:
@@ -590,9 +598,14 @@
sys.path_importer_cache[path_entry] = None
raise ImportError("no importer found for %s" % path_entry)
- def search_sys_path(self, name):
- """Check sys.path for the module and return a loader if found."""
- for entry in sys.path:
+ def search_std_path(self, name, path=None):
+ """Check sys.path or 'path' (depending if 'path' is set) for the
+ named module and return its loader."""
+ if path:
+ search_paths = path
+ else:
+ search_paths = sys.path
+ for entry in search_paths:
try:
importer = self.sys_path_importer(entry)
except ImportError:
@@ -603,7 +616,7 @@
else:
raise ImportError("%s not found on sys.path" % name)
- def import_(self, name):
+ def import_(self, name, path=None):
"""Import a module."""
try:
# Attempt to get a cached version of the module from sys.modules.
@@ -612,14 +625,14 @@
pass
try:
# Attempt to find a loader on sys.meta_path.
- loader = self.search_meta_path(name)
+ loader = self.search_meta_path(name, path)
except ImportError:
# sys.meta_path search failed. Attempt to find a loader on
# sys.path. If this fails then module cannot be found.
- loader = self.search_sys_path(name)
+ loader = self.search_std_path(name, path)
# A loader was found. It is the loader's responsibility to have put an
# entry in sys.modules.
- return loader.load_module(name)
+ return loader.load_module(name, path)
def __call__(self, name, globals={}, locals={}, fromlist=[], level=-1):
"""Import a module after resolving relative/absolute import issues.
@@ -646,9 +659,28 @@
# XXX Check for a relative import; if it is one make it absolute to try to import that,
# otherwise import as a top-level module.
- # XXX Import submodules; short-circuits search if module is already
- # in sys.modules.
- module = self.import_(name)
+ # Import the module (importing its parent modules first).
+ name_parts = name.split('.')
+ current_name_parts = []
+ parent_module = None
+ path_list = None
+ # XXX Any perk to being optimistic and assuming parent modules were
+ # imported already, and thus search in the reverse order for the first module
+ # to be found?
+ for name_part in name_parts:
+ current_name_parts.append(name_part)
+ # Recreating name every time around the loop is sub-optimal, but
+ # does away with base case of a non-dotted name.
+ current_name = ".".join(current_name_parts)
+ if parent_module:
+ try:
+ path_list = parent_module.__path__
+ except AttributeError:
+ pass
+ module = self.import_(current_name, path_list)
+ if parent_module:
+ setattr(parent_module, current_name, module)
+ parent_module = module
# When fromlist is not specified, return the root module (i.e., module
# up to first dot).
if not fromlist:
@@ -656,4 +688,4 @@
# When fromlist is not empty, return the actual module specified in
# the import.
else:
- return module
\ No newline at end of file
+ return sys.modules[name]
\ No newline at end of file
Modified: sandbox/trunk/import_in_py/mock_importer.py
==============================================================================
--- sandbox/trunk/import_in_py/mock_importer.py (original)
+++ sandbox/trunk/import_in_py/mock_importer.py Thu Nov 2 22:43:17 2006
@@ -215,8 +215,12 @@
"""Mock importer that always succeed by returning 'self'."""
module = 42
+
+ def __init__(self):
+ self.path_entries = []
def __call__(self, path_entry):
+ self.path_entries.append(path_entry)
return self
def find_module(self, fullname, path=None):
Modified: sandbox/trunk/import_in_py/test_importer.py
==============================================================================
--- sandbox/trunk/import_in_py/test_importer.py (original)
+++ sandbox/trunk/import_in_py/test_importer.py Thu Nov 2 22:43:17 2006
@@ -540,7 +540,7 @@
sys.path_hooks = self.old_path_hooks
sys.path_importer_cache = self.old_path_importer_cache
- def clear_sys_module(*modules):
+ def clear_sys_modules(*modules):
for module in modules:
try:
del sys.modules[module]
@@ -564,7 +564,12 @@
module = self.importer.import_('<test>')
del sys.modules[test_module]
self.failUnlessEqual(module, test_module)
-
+
+ def test_parent_missing(self):
+ # An import should fail if a parent module cannot be found.
+ sys.modules['a.b'] = 'a.b'
+ self.failUnlessRaises(ImportError, self.importer, 'a.b')
+
def test_empty_fromlist(self):
# An empty fromlist means that the root module is returned.
# XXX
@@ -583,7 +588,7 @@
def test_search_meta_path(self):
# Test search method of sys.meta_path.
# Should raise ImportError on error.
- self.clear_sys_module('sys')
+ self.clear_sys_modules('sys')
self.failUnlessRaises(ImportError, self.importer.search_meta_path,
'sys')
# Verify call order.
@@ -598,7 +603,7 @@
def test_extended_meta_path(self):
# Default meta_path entries set during initialization should be
# queried after sys.meta_path.
- self.clear_sys_module('sys')
+ self.clear_sys_modules('sys')
pass_importer = mock_importer.PassImporter()
sys.meta_path = [pass_importer]
succeed_importer = mock_importer.SucceedImporter()
@@ -607,57 +612,78 @@
for meta_importer in (pass_importer, succeed_importer):
self.failUnlessEqual(meta_importer.find_request, ('sys', None))
self.failUnless(module is mock_importer.SucceedImporter.module)
+
+ def test_parent_path(self):
+ # If a parent module has __path__ defined it should be passed as an
+ # argument during importing.
+ test_path = ['<test path>']
+ pkg_module = imp.new_module('_test_pkg')
+ pkg_module.__path__ = test_path
+ sys.modules['_test_pkg'] = pkg_module
+ succeed_importer = mock_importer.SucceedImporter()
+ sys.meta_path.append(succeed_importer)
+ module_name = '_test_pkg.module'
+ lookup_args = (module_name, test_path)
+ self.importer(module_name)
+ self.failUnless(module_name in sys.modules)
+ self.failUnlessEqual(succeed_importer.find_request, lookup_args)
+ self.failUnlessEqual(succeed_importer.load_request, lookup_args)
-class ImportSysPathTests(ImportHelper):
+class ImportStdPathTests(ImportHelper):
"""Test sys.path usage."""
def test_default_importer_factory(self):
# Make sure that the object passed in during initialization is used
# when sys.path_importer_cache has a value of None.
- self.clear_sys_module('sys')
+ module_name = '<dummy>'
+ self.clear_sys_modules(module_name)
succeed_importer = mock_importer.SucceedImporter()
importer_ = importer.Import(succeed_importer, tuple())
sys.meta_path = []
sys.path = ['<succeed>']
sys.path_importer_cache['<succeed>'] = None
- module = importer_.import_('sys')
- self.failUnlessEqual(succeed_importer.find_request, ('sys', None))
+ module = importer_.import_(module_name)
+ self.failUnlessEqual(succeed_importer.find_request,
+ (module_name, None))
self.failUnless(module is mock_importer.SucceedImporter.module)
- def test_search_sys_path(self):
+ def test_search_std_path(self):
# Test sys.path searching for a loader.
- self.clear_sys_module('sys')
+ module_name = '<dummy>'
+ self.clear_sys_modules(module_name)
importer_ = importer.Import(extended_meta_path=())
sys.path = []
sys_path = (mock_importer.PassImporter.set_on_sys_path(),
mock_importer.SucceedImporter.set_on_sys_path())
- module = importer_.import_('token')
+ module = importer_.import_(module_name)
for entry in sys_path:
- self.failUnlessEqual(entry.find_request, ('token', None))
+ self.failUnlessEqual(entry.find_request, (module_name, None))
self.failUnless(module is mock_importer.SucceedImporter.module)
def test_importer_cache_preexisting(self):
# A pre-existing importer should be returned if it exists in
# sys.path_importer_cache.
- self.clear_sys_module('sys')
+ module_name = '<dummy>'
+ self.clear_sys_modules(module_name)
sys.path = []
succeed_importer = mock_importer.SucceedImporter.set_on_sys_path()
- loader = self.importer.search_sys_path('sys')
+ loader = self.importer.search_std_path(module_name)
self.failUnless(loader is succeed_importer)
def test_importer_cache_from_path_hooks(self):
# If an entry does not exist for a sys.path entry in the importer cache
# then sys.path_hooks should be searched and if one is found then cache
# it.
- self.clear_sys_module('sys')
+ module_name = '<dummy>'
+ self.clear_sys_modules(module_name)
path_entry = '<succeed>'
succeed_importer = mock_importer.SucceedImporter()
sys.path = [path_entry]
sys.path_importer_cache.clear()
sys.path_hooks = [succeed_importer]
- loader = self.importer.search_sys_path('sys')
+ loader = self.importer.search_std_path(module_name)
self.failUnless(loader is succeed_importer)
self.failUnless(sys.path_importer_cache[path_entry] is
succeed_importer)
@@ -665,14 +691,34 @@
def test_importer_cache_no_path_hooks(self):
# If an entry does not exist for a sys.path entry in the importer cache
# and sys.path_hooks has nothing for the entry, None should be set.
- self.clear_sys_module('sys')
+ module_name = '<dummy>'
+ self.clear_sys_modules(module_name)
path_entry = '<test>'
sys.path = [path_entry]
sys.path_hooks = []
sys.path_importer_cache.clear()
- self.failUnlessRaises(ImportError, self.importer.search_sys_path, 'sys')
+ self.failUnlessRaises(ImportError, self.importer.search_std_path,
+ module_name)
self.failUnless(sys.path_importer_cache[path_entry] is None)
+ def test_searching_package_path(self):
+ # If importing in a package then search path is the package's __path__
+ # value; otherwise it is sys.path.
+ succeed_importer = mock_importer.SucceedImporter()
+ sys.path_hooks.append(succeed_importer)
+ search_paths = ['test path']
+ module_name = '<pkg>.<dummy>'
+ loader = self.importer.search_std_path(module_name, search_paths)
+ self.failUnless(loader is succeed_importer)
+ self.failUnless(search_paths[0] in succeed_importer.path_entries)
+ self.failUnlessEqual(succeed_importer.find_request,
+ (module_name, None))
+ self.clear_sys_modules(module_name)
+ del sys.path_importer_cache[search_paths[0]]
+ succeed_importer.path_entries = []
+ self.importer.import_(module_name, search_paths)
+ self.failUnless(search_paths[0] in succeed_importer.path_entries)
+
class IntegrationTests(unittest.TestCase):
@@ -693,7 +739,7 @@
# built-in, frozen, extension, .pyc, and .py files being imported if
# desired.
sys.path_importer_cache = dict((entry, None) for entry in sys.path)
- self.clear_sys_module('sys', '__hello__', 'time', 'token')
+ self.clear_sys_modules('sys', '__hello__', 'time', 'token')
# Restore sys.path for 'time' import.
sys.path = self.old_sys_path
import_ = importer.Import()
More information about the Python-checkins
mailing list