[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

AltStyle によって変換されたページ (->オリジナル) /