Rewrite the code implementing __import__ for importlib. Now it is much simpler

and relies much more on meta path finders to abstract out various parts of
import.

As part of this the semantics for import_module tightened up and now follow
__import__ much more closely (biggest thing is that the 'package' argument must
now already be imported, else a SystemError is raised).
This commit is contained in:
Brett Cannon 2009-02-07 01:15:27 +00:00
parent 887b3f2625
commit 2c318a1390
9 changed files with 117 additions and 502 deletions

View file

@ -38,8 +38,9 @@ class Using__package__(unittest.TestCase):
with util.mock_modules('pkg.__init__', 'pkg.fake') as importer:
with util.import_state(meta_path=[importer]):
import_util.import_('pkg.fake')
module = import_util.import_('', globals={'__package__': 'pkg.fake'},
fromlist=['attr'], level=2)
module = import_util.import_('',
globals={'__package__': 'pkg.fake'},
fromlist=['attr'], level=2)
self.assertEquals(module.__name__, 'pkg')
def test_using___name__(self):
@ -82,7 +83,7 @@ class Setting__package__(unittest.TestCase):
with util.import_state(meta_path=[mock]):
del mock['top_level'].__package__
module = import_util.import_('top_level')
self.assert_(module.__package__ is None)
self.assertEqual(module.__package__, '')
# [package]
def test_package(self):

View file

@ -64,7 +64,8 @@ class UseCache(unittest.TestCase):
with util.import_state(meta_path=[importer]):
module = import_util.import_('pkg', fromlist=['module'])
self.assert_(hasattr(module, 'module'))
self.assertEquals(id(module.module), id(sys.modules['pkg.module']))
self.assertEquals(id(module.module),
id(sys.modules['pkg.module']))
def test_main():

View file

@ -10,148 +10,6 @@ from types import MethodType
import unittest
class BaseTests(unittest.TestCase):
"""When sys.meta_path cannot find the desired module, sys.path is
consulted. For each entry on the sequence [order], sys.path_importer_cache
is checked to see if it contains a key for the entry [cache check]. If an
importer is found then it is consulted before trying the next entry in
sys.path [cache use]. The 'path' argument to find_module() is never used
when trying to find a module [path not used].
If an entry from sys.path is not in sys.path_importer_cache, sys.path_hooks
is called in turn [hooks order]. If a path hook cannot handle an entry,
ImportError is raised [hook failure]. Otherwise the resulting object is
cached in sys.path_importer_cache and then consulted [hook success]. If no
hook is found, None is set in sys.path_importer_cache and the default
importer is tried [no hook].
For use of __path__ in a package, the above is all true, just substitute
"sys.path" for "__path__".
"""
def order_test(self, to_import, entry, search_path, path=[]):
# [order]
log = []
class LogFindModule(util.mock_modules):
def find_module(self, fullname):
log.append(self)
return super().find_module(fullname)
assert len(search_path) == 2
misser = LogFindModule(search_path[0])
hitter = LogFindModule(to_import)
with nested(misser, hitter):
cache = dict(zip(search_path, (misser, hitter)))
with util.import_state(path=path, path_importer_cache=cache):
import_util.import_(to_import)
self.assertEquals(log[0], misser)
self.assertEquals(log[1], hitter)
@import_util.importlib_only # __import__ uses PyDict_GetItem(), bypassing log.
def cache_use_test(self, to_import, entry, path=[]):
# [cache check], [cache use]
log = []
class LoggingDict(dict):
def __getitem__(self, item):
log.append(item)
return super(LoggingDict, self).__getitem__(item)
with util.mock_modules(to_import) as importer:
cache = LoggingDict()
cache[entry] = importer
with util.import_state(path=[entry], path_importer_cache=cache):
module = import_util.import_(to_import, fromlist=['a'])
self.assert_(module is importer[to_import])
self.assertEquals(len(cache), 1)
self.assertEquals([entry], log)
def hooks_order_test(self, to_import, entry, path=[]):
# [hooks order], [hooks failure], [hook success]
log = []
def logging_hook(entry):
log.append(entry)
raise ImportError
with util.mock_modules(to_import) as importer:
hitter = import_util.mock_path_hook(entry, importer=importer)
path_hooks = [logging_hook, logging_hook, hitter]
with util.import_state(path_hooks=path_hooks, path=path):
import_util.import_(to_import)
self.assertEquals(sys.path_importer_cache[entry], importer)
self.assertEquals(len(log), 2)
# [no hook] XXX Worry about after deciding how to handle the default hook.
def path_argument_test(self, to_import):
# [path not used]
class BadImporter:
"""Class to help detect TypeError from calling find_module() with
an improper number of arguments."""
def find_module(name):
raise ImportError
try:
import_util.import_(to_import)
except ImportError:
pass
class PathTests(BaseTests):
"""Tests for sys.path."""
def test_order(self):
self.order_test('hit', 'second', ['first', 'second'],
['first', 'second'])
def test_cache_use(self):
entry = "found!"
self.cache_use_test('hit', entry, [entry])
def test_hooks_order(self):
entry = "found!"
self.hooks_order_test('hit', entry, [entry])
def test_path_argument(self):
name = 'total junk'
with util.uncache(name):
self.path_argument_test(name)
class __path__Tests(BaseTests):
"""Tests for __path__."""
def run_test(self, test, entry, path, *args):
with util.mock_modules('pkg.__init__') as importer:
importer['pkg'].__path__ = path
importer.load_module('pkg')
test('pkg.hit', entry, *args)
@import_util.importlib_only # XXX Unknown reason why this fails.
def test_order(self):
self.run_test(self.order_test, 'second', ('first', 'second'), ['first',
'second'])
def test_cache_use(self):
location = "I'm here!"
self.run_test(self.cache_use_test, location, [location])
def test_hooks_order(self):
location = "I'm here!"
self.run_test(self.hooks_order_test, location, [location])
def test_path_argument(self):
module = imp.new_module('pkg')
module.__path__ = ['random __path__']
name = 'pkg.whatever'
sys.modules['pkg'] = module
with util.uncache('pkg', name):
self.path_argument_test(name)
class FinderTests(unittest.TestCase):
"""Tests for SysPathImporter."""

View file

@ -1,5 +1,5 @@
import functools
import importlib
import importlib._bootstrap
using___import__ = False
@ -9,7 +9,8 @@ def import_(*args, **kwargs):
"""Delegate to allow for injecting different implementations of import."""
if using___import__:
return __import__(*args, **kwargs)
return importlib.Import()(*args, **kwargs)
#return importlib.Import()(*args, **kwargs)
return importlib._bootstrap._import(*args, **kwargs)
def importlib_only(fxn):