[Python-checkins] r52575 - 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
Wed Nov 1 23:57:04 CET 2006
Author: brett.cannon
Date: Wed Nov 1 23:57:03 2006
New Revision: 52575
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:
Refactor unit tests to use mock objects. Tests now run faster and are more
isolated.
Also begin initial work on package support (mostly untested).
Modified: sandbox/trunk/import_in_py/importer.py
==============================================================================
--- sandbox/trunk/import_in_py/importer.py (original)
+++ sandbox/trunk/import_in_py/importer.py Wed Nov 1 23:57:03 2006
@@ -255,6 +255,7 @@
def __init__(self, path_entry, *handlers):
self.path_entry = path_entry
self.handlers = handlers
+ self.loader = FileSystemLoader
def find_module(self, fullname, path=None):
"""Determine if this path entry can handle this import, and if so,
@@ -262,15 +263,19 @@
registered."""
# XXX Ignores 'path' at the moment.
# XXX Does not worry about case-insensitive filesystems.
- try:
- for handler in self.handlers:
- for file_ext in handler.handles:
- file_name = fullname + file_ext
- file_path = os.path.join(self.path_entry, file_name)
- if os.path.isfile(file_path):
- raise StopIteration("file found")
- except StopIteration:
- return FileSystemLoader(file_path, handler)
+ for handler in self.handlers:
+ for file_ext in handler.handles:
+ # XXX Backwards-incompatible to use extension modules for __init__?
+ package_entry = os.path.join(self.path_entry, fullname)
+ package_name = os.path.join(package_entry,
+ '__init__'+file_ext)
+ file_path = os.path.join(self.path_entry, package_name)
+ if os.path.isfile(file_path):
+ return FileSystemLoader(file_path, handler, package_entry)
+ file_name = fullname + file_ext
+ file_path = os.path.join(self.path_entry, file_name)
+ if os.path.isfile(file_path):
+ return self.loader(file_path, handler)
else:
return None
@@ -283,10 +288,11 @@
"""
- def __init__(self, file_path, handler):
+ def __init__(self, file_path, handler, package=None):
"""Store arguments on to the instance."""
self.file_path = file_path
self.handler = handler
+ self.package = package
def load_module(self, fullname, path=None):
"""Load the module from self.path using self.handler.
@@ -305,6 +311,8 @@
if fullname in sys.modules:
del sys.modules[fullname]
raise
+ if self.package is not None:
+ module.__path__ = [self.package]
return module
def mod_time(self, path):
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 Wed Nov 1 23:57:03 2006
@@ -11,6 +11,19 @@
return log_and_call
+class MockHandler(object):
+
+ """Mock a handler."""
+
+ def __init__(self, *handles):
+ self.handles = handles
+
+ def handle_code(self, loader, mod_name, path):
+ """Mock implementation of a handler."""
+ called_with = loader, mod_name, path
+ sys.modules[mod_name] = called_with
+ return called_with
+
class MockPyPycLoader(object):
"""Mock loader object for testing importer.PyPycHandler.
@@ -22,9 +35,9 @@
def __init__(self, file_path, handler, package=None):
"""Store arguments passed into the constructor for verification."""
- self.init_file_path = file_path
- self.init_handler = handler
- self.init_package = package
+ self.file_path = file_path
+ self.handler = handler
+ self.package = package
@classmethod
def setup(cls, good_magic=True, good_timestamp=True, pyc_exists=True,
@@ -89,7 +102,8 @@
return True
def load_module(self, fullname, path=None):
- raise NotImplementedError
+ """Return what the method was called with."""
+ return fullname, path
@log_call
def split_path(self, path):
@@ -149,9 +163,11 @@
return None
+# Mock Importers (with optional path_hooks support).
+
class ErrorImporter(object):
- """Helper class to have a guaranteed error point."""
+ """Mock importer to have a guaranteed error point."""
def find_module(self, fullname, path=None):
self.find_request = fullname, path
@@ -168,7 +184,11 @@
class PassImporter(object):
- """Always pass on importing a module."""
+ """Mock importer that always pass on importing a module."""
+
+ def __call__(self, path_entry):
+ """Always pass when asked to create an importer."""
+ raise ImportError
def find_module(self, fullname, path=None):
self.find_request = fullname, path
@@ -185,7 +205,7 @@
class SucceedImporter(object):
- """Always succeed by returning 'self'."""
+ """Mock importer that always succeed by returning 'self'."""
module = 42
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 Wed Nov 1 23:57:03 2006
@@ -1,3 +1,4 @@
+"""XXX separate medium/large tests into a single test class."""
from __future__ import with_statement
import importer
@@ -166,23 +167,37 @@
self.failUnlessEqual(getattr(module, self.attr_name), self.attr_value)
-class FileSystemImporterTests(PyPycFileHelper):
+class FileSystemImporterTests(unittest.TestCase):
"""Test the filesystem importer."""
def setUp(self):
"""Create a basic importer."""
- super(FileSystemImporterTests, self).setUp()
- source_handler = importer.PyPycHandler(bytecode_handles=tuple())
+ self.directory = tempfile.gettempdir()
+ self.module_name = 'importer_test'
+ self.file_ext = '.test'
+ self.file_path = os.path.join(self.directory,
+ self.module_name + self.file_ext)
+ with open(self.file_path, 'w') as test_file:
+ test_file.write("A file for testing the 'importer' module.")
+ mock_handler = mock_importer.MockHandler(self.file_ext)
self.importer = importer.FileSystemImporter(self.directory,
- source_handler)
+ mock_handler)
+ self.importer.loader = mock_importer.MockPyPycLoader
+
+ def tearDown(self):
+ """Clean up the created file."""
+ try:
+ os.remove(self.file_path)
+ except OSError:
+ pass
def test_find_module_single_handler(self):
# Having a single handler should work without issue.
- loader = self.importer.find_module(self.module)
- self.failUnless(isinstance(loader, importer.FileSystemLoader))
- self.failUnlessEqual(loader.file_path, self.source_path)
- self.failUnless(isinstance(loader.handler, importer.PyPycHandler))
+ loader = self.importer.find_module(self.module_name)
+ self.failUnless(isinstance(loader, mock_importer.MockPyPycLoader))
+ self.failUnlessEqual(loader.file_path, self.file_path)
+ self.failUnless(isinstance(loader.handler, mock_importer.MockHandler))
def test_find_module_cannot_find(self):
# Should return None if it can't find the module.
@@ -191,48 +206,58 @@
def test_find_module_multiple_handlers(self):
# Modules should be found based on the order of the handlers.
- source_handler = importer.PyPycHandler(bytecode_handles=tuple())
- bytecode_handler = importer.PyPycHandler(source_handles=tuple())
+ # mock
+ first_handler = mock_importer.MockHandler('.not_found')
+ second_handler = mock_importer.MockHandler(self.file_ext)
fs_importer = importer.FileSystemImporter(self.directory,
- bytecode_handler, source_handler)
- loader = fs_importer.find_module(self.module)
- self.failUnless(isinstance(loader, importer.FileSystemLoader))
- self.failUnlessEqual(loader.file_path, self.bytecode_path)
- self.failUnless(isinstance(loader.handler, importer.PyPycHandler))
-
- def test_find_to_load(self):
- # Make sure that one can go from find_module() to getting a module
- # imported.
- loader = self.importer.find_module(self.module)
- self.failUnless(loader)
- module = loader.load_module(self.module)
- self.verify_module(module, self.source_path)
- self.failUnlessEqual(module, sys.modules[self.module])
-
+ first_handler,
+ second_handler)
+ fs_importer.loader = mock_importer.MockPyPycLoader
+ loader = fs_importer.find_module(self.module_name)
+ self.failUnless(isinstance(loader, mock_importer.MockPyPycLoader))
+ self.failUnlessEqual(loader.file_path, self.file_path)
+ self.failUnless(isinstance(loader.handler, mock_importer.MockHandler))
-class FileSystemLoaderBlackBoxTests(PyPycFileHelper):
- """Test the filesystem loader's PEP 302 API compliance."""
+class FileSystemLoaderMockEnv(unittest.TestCase):
+
+ """Helper for settting up mock environment for testing
+ importer.FileSystemLoader."""
def setUp(self):
"""Create a fresh loader per run."""
- super(FileSystemLoaderBlackBoxTests, self).setUp()
- source_handler = importer.PyPycHandler(bytecode_handles=tuple())
- self.loader = importer.FileSystemLoader(self.source_path,
- source_handler)
+ # XXX mock
+ mock_handler = mock_importer.MockHandler()
+ self.test_path = "<test path>"
+ self.module_name = "test_module_name"
+ self.loader = importer.FileSystemLoader(self.test_path,
+ mock_handler)
+
+ def tearDown(self):
+ """Make sure that there is no straggling modules in sys.modules."""
+ try:
+ del sys.modules[self.module_name]
+ except KeyError:
+ pass
+
+
+class FileSystemLoaderBlackBoxTests(FileSystemLoaderMockEnv):
+
+ """Test the filesystem loader's PEP 302 API compliance."""
def test_load_module_fresh(self):
# Test a basic module load where there is no sys.modules entry.
# PyPycFileHelper.setUp() clears sys.modules for us.
- new_module = self.loader.load_module(self.module)
- self.verify_module(new_module, self.source_path)
+ new_module = self.loader.load_module(self.module_name)
+ expected = self.loader, self.module_name, self.test_path
+ self.failUnlessEqual(new_module, expected)
def test_load_module_sys_modules(self):
# Make sure that the loader returns the module from sys.modules if it
# is there.
- sys.modules[self.module] = self.module_object
- loaded_module = self.loader.load_module(self.module)
- self.failUnless(loaded_module is self.module_object)
+ sys.modules[self.module_name] = self.module_name
+ loaded_module = self.loader.load_module(self.module_name)
+ self.failUnless(loaded_module is self.module_name)
def test_sys_module_cleared_on_error(self):
# Any entry made for module into sys.modules should be cleared upon error.
@@ -240,64 +265,77 @@
def handle_code(*args):
raise ImportError
- loader = importer.FileSystemLoader(self.source_path, RaiseErrorHandler())
+ loader = importer.FileSystemLoader(self.test_path, RaiseErrorHandler())
try:
- loader.load_module(self.module)
+ loader.load_module(self.module_name)
except ImportError:
- self.failUnless(self.module not in sys.modules)
+ self.failUnless(self.module_name not in sys.modules)
+
+ def test_package_init(self):
+ # Test that instantiating a loader for handling a package works
+ # properly.
+ # XXX
+ pass
-class FileSystemLoaderWhiteBoxTests(PyPycFileHelper):
+class FileSystemLoaderWhiteBoxTests(FileSystemLoaderMockEnv):
"""Test the filesystem loader's non-PEP 302 API."""
def setUp(self):
- """Create a loader."""
+ """Set up a test file."""
super(FileSystemLoaderWhiteBoxTests, self).setUp()
- source_handler = importer.PyPycHandler(bytecode_handles=tuple())
- self.loader = importer.FileSystemLoader(self.source_path,
- source_handler)
+ self.directory = tempfile.gettempdir()
+ self.file_ext = ".test"
+ self.module_name = "import_test"
+ self.file_name = self.module_name + self.file_ext
+ self.file_path = os.path.join(self.directory, self.file_name)
+ self.data = "File used for testing 'importer' module.\r\n42"
+ with open(self.file_path, 'w') as test_file:
+ test_file.write(self.data)
+
+ def tearDown(self):
+ """Delete the test file."""
+ super(FileSystemLoaderWhiteBoxTests, self).tearDown()
+ try:
+ os.remove(self.file_path)
+ except OSError:
+ pass
def test_mod_time(self):
# Modification time for the passed-in path should match the file.
- true_mod_time = os.stat(self.source_path).st_mtime
- mod_time = self.loader.mod_time(self.source_path)
+ true_mod_time = os.stat(self.file_path).st_mtime
+ mod_time = self.loader.mod_time(self.file_path)
self.failUnlessEqual(mod_time, true_mod_time)
def test_split_path(self):
# Should split a path just like os.path.splitext().
- true_split_path = os.path.splitext(self.source_path)
- split_path = self.loader.split_path(self.source_path)
+ true_split_path = os.path.splitext(self.file_path)
+ split_path = self.loader.split_path(self.file_path)
self.failUnlessEqual(split_path, true_split_path)
def test_create_path(self):
# Should work like concatenating two strings.
- true_path = ''.join(self.loader.split_path(self.source_path))
+ true_path = self.file_path
path = self.loader.create_path(*self.loader.split_path(
- self.source_path))
+ self.file_path))
self.failUnlessEqual(path, true_path)
def test_read_data(self):
# Should be able to read a file.
- source = self.loader.read_data(self.source_path, False)
- self.failUnlessEqual(source, self.source)
+ data = self.loader.read_data(self.file_path, False)
+ self.failUnlessEqual(data, self.data.replace('\r', ''))
+ data = self.loader.read_data(self.file_path, True)
+ self.failUnlessEqual(data, self.data)
test_text = '1\r\n2'
- try:
- with open(test_support.TESTFN, 'wb') as test_file:
- test_file.write(test_text)
- result = self.loader.read_data(test_support.TESTFN, binary=True)
- self.failUnlessEqual(result, test_text)
- result = self.loader.read_data(test_support.TESTFN, binary=False)
- self.failUnlessEqual(result, test_text.replace('\r', ''))
- finally:
- os.remove(test_support.TESTFN)
def test_write_data(self):
# Should be able to write a file.
- self.loader.write_data(self.source, self.source_path, False)
- read_source = self.loader.read_data(self.source_path, False)
- self.failUnlessEqual(read_source, self.source)
- # XXX How to test data written with 'b'?
+ self.loader.write_data(self.data, self.file_path, True)
+ with open(self.file_path, 'rb') as test_file:
+ data = test_file.read()
+ self.failUnlessEqual(data, self.data)
+ # XXX How to test data written with/without 'b'?
class PyPycHandlerSupportingMethodTests(PyPycFileHelper):
@@ -436,7 +474,7 @@
raise test_support.TestSkipped("not extension modules found")
self.ext_path = os.path.join(entry, ext_paths[0])
self.module_name = os.path.splitext(os.path.split(self.ext_path)[1])[0]
- self.loader = importer.FileSystemLoader(self.ext_path, self.handler)
+ self.loader = mock_importer.MockHandler()
def test_handle_code(self):
# Make sure an extension module can be loaded.
@@ -465,7 +503,8 @@
sys.path_hooks = []
self.old_path_importer_cache = sys.path_importer_cache.copy()
sys.path_importer_cache.clear()
- self.import_ = importer.Importer()
+ self.default_importer = mock_importer.PassImporter()
+ self.importer = importer.Importer(self.default_importer, tuple())
def tearDown(self):
"""Restore backup of import-related attributes in 'sys'."""
@@ -486,46 +525,21 @@
class ImporterMiscTests(ImporterHelper):
- """Test miscellaneous parts of the Importer class."""
+ """Test miscellaneous parts of the Importer class.
+
+ Tests of the default values for __init__ are handled in the integration
+ tests.
+
+ """
def test_sys_module_return(self):
# A module found in sys.modules should be returned immediately.
- sys.path_importer_cache.clear()
- sys.path = []
test_module = '<test>'
sys.modules[test_module] = test_module
- module = self.import_('<test>')
+ module = self.importer.import_('<test>')
+ del sys.modules[test_module]
self.failUnlessEqual(module, test_module)
-
- def test_default_init(self):
- # The default initialization should work with a None entry for every
- # sys.path entry in sys.path_importer_cache. It should also lead to
- # 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')
- # Restore sys.path for 'time' import.
- sys.path = self.old_sys_path
- # Built-ins.
- module = self.import_('sys')
- self.failUnlessEqual(module.__name__, 'sys')
- self.failUnless(hasattr(sys, 'version'))
- # Frozen modules.
- try:
- sys.stdout = StringIO.StringIO()
- module = self.import_('__hello__')
- finally:
- sys.stdout = sys.__stdout__
- self.failUnlessEqual(module.__name__, '__hello__')
- # Extension modules.
- module = self.import_('time')
- self.failUnlessEqual(module.__name__, 'time')
- self.failUnless(hasattr(module, 'sleep'))
- # .py/.pyc files.
- module = self.import_('token')
- self.failUnlessEqual(module.__name__, 'token')
- self.failUnless(hasattr(module, 'ISTERMINAL'))
-
+
def test_empty_fromlist(self):
# An empty fromlist means that the root module is returned.
# XXX
@@ -545,15 +559,13 @@
# Test search method of sys.meta_path.
# Should raise ImportError on error.
self.clear_sys_module('sys')
- import_ = importer.Importer(extended_meta_path=())
- sys.meta_path = []
- self.failUnlessRaises(ImportError, import_.search_meta_path,
+ self.failUnlessRaises(ImportError, self.importer.search_meta_path,
'sys')
# Verify call order.
meta_path = (mock_importer.PassImporter(),
mock_importer.SucceedImporter())
sys.meta_path = meta_path
- loader = import_.search_meta_path('sys')
+ loader = self.importer.search_meta_path('sys')
for entry in meta_path:
self.failUnlessEqual(entry.find_request, ('sys', None))
self.failUnless(loader is meta_path[-1])
@@ -565,8 +577,8 @@
pass_importer = mock_importer.PassImporter()
sys.meta_path = [pass_importer]
succeed_importer = mock_importer.SucceedImporter()
- import_ = importer.Importer(extended_meta_path=(succeed_importer,))
- module = import_('sys')
+ importer_ = importer.Importer(extended_meta_path=(succeed_importer,))
+ module = importer_.import_('sys')
for meta_importer in (pass_importer, succeed_importer):
self.failUnlessEqual(meta_importer.find_request, ('sys', None))
self.failUnless(module is mock_importer.SucceedImporter.module)
@@ -581,22 +593,22 @@
# when sys.path_importer_cache has a value of None.
self.clear_sys_module('sys')
succeed_importer = mock_importer.SucceedImporter()
- import_ = importer.Importer(succeed_importer, ())
+ importer_ = importer.Importer(succeed_importer, tuple())
sys.meta_path = []
sys.path = ['<succeed>']
sys.path_importer_cache['<succeed>'] = None
- module = import_('sys')
+ module = importer_.import_('sys')
self.failUnlessEqual(succeed_importer.find_request, ('sys', None))
self.failUnless(module is mock_importer.SucceedImporter.module)
def test_search_sys_path(self):
# Test sys.path searching for a loader.
self.clear_sys_module('sys')
- import_ = importer.Importer(extended_meta_path=())
+ importer_ = importer.Importer(extended_meta_path=())
sys.path = []
sys_path = (mock_importer.PassImporter.set_on_sys_path(),
mock_importer.SucceedImporter.set_on_sys_path())
- module = import_('token')
+ module = importer_.import_('token')
for entry in sys_path:
self.failUnlessEqual(entry.find_request, ('token', None))
self.failUnless(module is mock_importer.SucceedImporter.module)
@@ -607,7 +619,7 @@
self.clear_sys_module('sys')
sys.path = []
succeed_importer = mock_importer.SucceedImporter.set_on_sys_path()
- loader = self.import_.search_sys_path('sys')
+ loader = self.importer.search_sys_path('sys')
self.failUnless(loader is succeed_importer)
def test_importer_cache_from_path_hooks(self):
@@ -620,7 +632,7 @@
sys.path = [path_entry]
sys.path_importer_cache.clear()
sys.path_hooks = [succeed_importer]
- loader = self.import_.search_sys_path('sys')
+ loader = self.importer.search_sys_path('sys')
self.failUnless(loader is succeed_importer)
self.failUnless(sys.path_importer_cache[path_entry] is
succeed_importer)
@@ -633,8 +645,52 @@
sys.path = [path_entry]
sys.path_hooks = []
sys.path_importer_cache.clear()
- self.failUnlessRaises(ImportError, self.import_.search_sys_path, 'sys')
+ self.failUnlessRaises(ImportError, self.importer.search_sys_path, 'sys')
self.failUnless(sys.path_importer_cache[path_entry] is None)
+
+
+class IntegrationTests(unittest.TestCase):
+
+ """Hold tests that involve multiple classes from 'importer' and verify
+ integration semantics.
+
+ XXX
+ * file loader <-> py/pyc handler
+ * file loader <-> extension handler
+ * file importer -> file loader
+ * file importer -> file loader <-> py/pyc handler
+ * Importer -> importer -> loader
+
+ """
+ def XXX_test_default_import(self):
+ # The default initialization should work with a None entry for every
+ # sys.path entry in sys.path_importer_cache. It should also lead to
+ # 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')
+ # Restore sys.path for 'time' import.
+ sys.path = self.old_sys_path
+ import_ = importer.Importer()
+ # Built-ins.
+ module = import_('sys')
+ self.failUnlessEqual(module.__name__, 'sys')
+ self.failUnless(hasattr(sys, 'version'))
+ # Frozen modules.
+ try:
+ sys.stdout = StringIO.StringIO()
+ module = import_('__hello__')
+ finally:
+ sys.stdout = sys.__stdout__
+ self.failUnlessEqual(module.__name__, '__hello__')
+ # Extension modules.
+ module = import_('time')
+ self.failUnlessEqual(module.__name__, 'time')
+ self.failUnless(hasattr(module, 'sleep'))
+ # .py/.pyc files.
+ module = import_('token')
+ self.failUnlessEqual(module.__name__, 'token')
+ self.failUnless(hasattr(module, 'ISTERMINAL'))
def test_main():
More information about the Python-checkins
mailing list