r52553 - sandbox/trunk/import_in_py/importer.py sandbox/trunk/import_in_py/mock_importer.py sandbox/trunk/import_in_py/test_importer.py
Author: brett.cannon Date: Mon Oct 30 03:21:21 2006 New Revision: 52553 Added: sandbox/trunk/import_in_py/mock_importer.py (contents, props changed) Modified: sandbox/trunk/import_in_py/importer.py sandbox/trunk/import_in_py/test_importer.py Log: Add substantive tests for PyPycHandler.handle_code(). This includes creating a mock object for the class and creating the mock_importer class to contain it. Also moved over other mock objects from test_importer into mock_importer. Modified: sandbox/trunk/import_in_py/importer.py ============================================================================== --- sandbox/trunk/import_in_py/importer.py (original) +++ sandbox/trunk/import_in_py/importer.py Mon Oct 30 03:21:21 2006 @@ -370,13 +370,13 @@ def code_from_source(self, source, path): """Create a code object from source.""" - return compile(source, path, 'exec') + return compile(source, str(path), 'exec') def create_pyc(self, bytecode, timestamp): """Create data for a .pyc file.""" #XXX Does not work properly until direct marshaling of longs is possible. - data = imp.get_magic() - data += str(timestamp) + data = imp.get_magic().zfill(4) + data += str(timestamp).zfill(4) data += marshal.dumps(bytecode) return data @@ -463,7 +463,7 @@ # If there is a possible path to bytecode, generate .pyc file. if bytecode_path: pyc = self.create_pyc(code, source_timestamp) - loader.write_data(pyc, path, True) + loader.write_data(pyc, bytecode_path, True) exec code in module.__dict__ return module Added: sandbox/trunk/import_in_py/mock_importer.py ============================================================================== --- (empty file) +++ sandbox/trunk/import_in_py/mock_importer.py Mon Oct 30 03:21:21 2006 @@ -0,0 +1,193 @@ +import sys +import marshal +import imp +from test import test_support + +def log_call(method): + """Log method calls to self.log.""" + def log_and_call(self, *args, **kwargs): + self.log.append(method.__name__) + return method(self, *args, **kwargs) + return log_and_call + + +class MockPyPycLoader(object): + + """Mock loader object for testing importer.PyPycHandler. + + Opaque path objects are two-item tuple consisting of a string representing + the base path and another string representing the type. + + """ + + def __init__(self, good_magic=True, good_timestamp=True, pyc_exists=True, + py_exists=True): + """Set up the mock loader based on possible scenarios of source and + bytecode combinations/issues.""" + self.module_name = 'test_module' + self.py_ext = "source" if py_exists else None + self.pyc_ext = "bytecode" if pyc_exists else None + self.base_path = "base path" + self.modification_time = 1 + # Needed for read_data on source path. + self.source = "test_attr = None" + code_object = compile(self.source, "<mock loader>", 'exec') + bytecode = marshal.dumps(code_object) + if good_magic: + pyc = str(imp.get_magic()) + else: + pyc += str(imp.get_magic()-1) + if good_timestamp: + pyc += str(1).zfill(4) + else: + pyc += str(0).zfill(4) + # XXX Not proper until can marshal longs. + # Needed for read_data on .pyc path. + self.pyc = pyc + bytecode + self.log = [] + + def _create_handler(self, handler): + if self.py_ext: + py_ext = (self.py_ext,) + else: + py_ext = tuple() + if self.pyc_ext: + pyc_ext = (self.pyc_ext,) + else: + pyc_ext = tuple() + return handler(py_ext, pyc_ext) + + def _handle_py(self, handler): + return handler.handle_code(self, self.module_name, + (self.base_path, self.py_ext)) + + def _handle_pyc(self, handler): + return handler.handle_code(self, self.module_name, + (self.base_path, self.pyc_ext)) + + def _verify_module(self, module): + if not hasattr(module, 'test_attr'): + raise test_support.TestFailed("test_attr attribute missing") + if not module.test_attr is None: + raise test_support.TestFailed("test_attr not set to None") + return True + + def load_module(self, fullname, path=None): + raise NotImplementedError + + @log_call + def split_path(self, path): + """'path' should be a two-item tuple, so just return it since the + caller expects two items back.""" + assert (path[0] == self.base_path and + path[1] in (self.py_ext, self.pyc_ext)) + + return path + + @log_call + def create_path(self, base, type_): + """Create a tuple from 'base' and type_.""" + assert base == self.base_path + assert type_ in (self.py_ext, self.pyc_ext) + + if type_ not in (self.py_ext, self.pyc_ext): + return None + else: + return (base, type_) + + @log_call + def mod_time(self, path): + """Consider self.modification_time the baseline modification time.""" + assert path[0] == self.base_path + assert path[1] == self.py_ext + + return self.modification_time + + @log_call + def read_data(self, path, binary): + """Return the proper source or binary data based on the path.""" + assert path[0] == self.base_path + assert path[1] in (self.py_ext, self.pyc_ext) + + if binary: + assert self.pyc_ext and path[1] == self.pyc_ext + return self.pyc + else: + assert self.py_ext and path[1] == self.py_ext + return self.source + + @log_call + def write_data(self, data, path, binary): + """Should only be requested to write to a bytecode path.""" + assert path[0] == self.base_path + assert self.pyc_ext + assert path[1] == self.pyc_ext, "%s != %s" % (path[1], self.pyc_ext) + assert binary + # XXX Assert proper magic number + # XXX Assert time stamp + module = imp.new_module(self.module_name) + code = marshal.loads(data[8:]) + exec code in module.__dict__ + assert self._verify_module(module) + + return None + + +class ErrorImporter(object): + + """Helper class to have a guaranteed error point.""" + + def find_module(self, fullname, path=None): + self.find_request = fullname, path + raise ImportError + + @classmethod + def set_on_sys_path(cls): + error_entry = '<error>' + sys.path.append(error_entry) + ins = cls() + sys.path_importer_cache[error_entry] = ins + return ins + + +class PassImporter(object): + + """Always pass on importing a module.""" + + def find_module(self, fullname, path=None): + self.find_request = fullname, path + return None + + @classmethod + def set_on_sys_path(cls): + pass_entry = '<pass>' + sys.path.append(pass_entry) + ins = cls() + sys.path_importer_cache[pass_entry] = ins + return ins + + +class SucceedImporter(object): + + """Always succeed by returning 'self'.""" + + module = 42 + + def __call__(self, path_entry): + return self + + def find_module(self, fullname, path=None): + self.find_request = fullname, path + return self + + def load_module(self, fullname, path=None): + self.load_request = fullname, path + return self.module + + @classmethod + def set_on_sys_path(cls): + succeed_entry = '<success>' + sys.path.append(succeed_entry) + ins = cls() + sys.path_importer_cache[succeed_entry] = ins + return ins \ No newline at end of file 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 Mon Oct 30 03:21:21 2006 @@ -3,6 +3,7 @@ import unittest from test import test_support +import mock_importer import contextlib import imp @@ -351,15 +352,53 @@ # Test creating bytes for creating a .pyc file. # XXX Cannot test until marshaling longs is possible. pass - + class PyPycHandlerHandleCodeTests(unittest.TestCase): """Test PyPycHandler.handle_code().""" - # XXX - pass + def test_good_pyc_w_py(self): + loader = mock_importer.MockPyPycLoader() + handler = loader._create_handler(importer.PyPycHandler) + module = loader._handle_pyc(handler) + loader._verify_module(module) + # Source code should never be read, only bytecode. + self.failUnlessEqual(loader.log.count('read_data'), 1) + # Bytecode should not be written. + self.failUnless("write_data" not in loader.log) + + def test_bad_magic_no_py(self): + # XXX Can't test until can unmarshal magic number from pyc. + pass + + def test_bad_timestamp_no_py(self): + # XXX Can't test until can unmarshal timestamp from pyc. + pass + + def test_bad_magic_w_py(self): + # XXX Can't test until can unmarhsal magic number from pyc. + pass + + def test_bad_magic_w_py(self): + # XXX Can't test until can unmarshal timestamp from pyc. + pass + def test_py_no_pyc(self): + loader = mock_importer.MockPyPycLoader(pyc_exists=False) + handler = loader._create_handler(importer.PyPycHandler) + module = loader._handle_py(handler) + loader._verify_module(module) + self.failUnless('write_data' not in loader.log) + # No attempt should be made to read bytecode. + self.failUnlessEqual(loader.log.count('read_data'), 1) + + def test_py_w_pyc(self): + loader = mock_importer.MockPyPycLoader() + handler = loader._create_handler(importer.PyPycHandler) + module = loader._handle_py(handler) + loader._verify_module(module) + self.failUnless('write_data' in loader.log) class ExtensionHandlerTests(unittest.TestCase): @@ -392,64 +431,6 @@ if not attr.startswith('_'))) -class ErrorImporter(object): - - """Helper class to have a guaranteed error point.""" - - def find_module(self, fullname, path=None): - self.find_request = fullname, path - raise ImportError - - @classmethod - def set_on_sys_path(cls): - error_entry = '<error>' - sys.path.append(error_entry) - ins = cls() - sys.path_importer_cache[error_entry] = ins - return ins - -class PassImporter(object): - - """Always pass on importing a module.""" - - def find_module(self, fullname, path=None): - self.find_request = fullname, path - return None - - @classmethod - def set_on_sys_path(cls): - pass_entry = '<pass>' - sys.path.append(pass_entry) - ins = cls() - sys.path_importer_cache[pass_entry] = ins - return ins - -class SucceedImporter(object): - - """Always succeed by returning 'self'.""" - - module = 42 - - def __call__(self, path_entry): - return self - - def find_module(self, fullname, path=None): - self.find_request = fullname, path - return self - - def load_module(self, fullname, path=None): - self.load_request = fullname, path - return self.module - - @classmethod - def set_on_sys_path(cls): - succeed_entry = '<success>' - sys.path.append(succeed_entry) - ins = cls() - sys.path_importer_cache[succeed_entry] = ins - return ins - - class SimpleImportTests(unittest.TestCase): """Test Importer class with only direct module imports; no packages.""" @@ -477,26 +458,26 @@ 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. - succeed_importer = SucceedImporter() + succeed_importer = mock_importer.SucceedImporter() import_ = importer.Importer(succeed_importer, ()) sys.meta_path = [] sys.path = ['<succeed>'] sys.path_importer_cache['<succeed>'] = None module = import_('sys') self.failUnlessEqual(succeed_importer.find_request, ('sys', None)) - self.failUnless(module is SucceedImporter.module) + self.failUnless(module is mock_importer.SucceedImporter.module) def test_extended_meta_path(self): # Default meta_path entries set during initialization should be # queried after sys.meta_path. - pass_importer = PassImporter() + pass_importer = mock_importer.PassImporter() sys.meta_path = [pass_importer] - succeed_importer = SucceedImporter() + succeed_importer = mock_importer.SucceedImporter() import_ = importer.Importer(extended_meta_path=(succeed_importer,)) module = import_('sys') for meta_importer in (pass_importer, succeed_importer): self.failUnlessEqual(meta_importer.find_request, ('sys', None)) - self.failUnless(module is SucceedImporter.module) + self.failUnless(module is mock_importer.SucceedImporter.module) def test_default_init(self): # The default initialization should work with a None entry for every @@ -533,7 +514,8 @@ self.failUnlessRaises(ImportError, import_.search_meta_path, 'sys') # Verify call order. - meta_path = PassImporter(), SucceedImporter() + meta_path = (mock_importer.PassImporter(), + mock_importer.SucceedImporter()) sys.meta_path = meta_path loader = import_.search_meta_path('sys') for entry in meta_path: @@ -545,18 +527,18 @@ sys.meta_path = [] import_ = importer.Importer(extended_meta_path=()) sys.path = [] - sys_path = (PassImporter.set_on_sys_path(), - SucceedImporter.set_on_sys_path()) + sys_path = (mock_importer.PassImporter.set_on_sys_path(), + mock_importer.SucceedImporter.set_on_sys_path()) module = import_('token') for entry in sys_path: self.failUnlessEqual(entry.find_request, ('token', None)) - self.failUnless(module is SucceedImporter.module) + 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. sys.path = [] - succeed_importer = SucceedImporter.set_on_sys_path() + succeed_importer = mock_importer.SucceedImporter.set_on_sys_path() loader = self.import_.search_sys_path('sys') self.failUnless(loader is succeed_importer) @@ -565,7 +547,7 @@ # then sys.path_hooks should be searched and if one is found then cache # it. path_entry = '<succeed>' - succeed_importer = SucceedImporter() + succeed_importer = mock_importer.SucceedImporter() sys.path = [path_entry] sys.path_importer_cache.clear() sys.path_hooks = [succeed_importer]
participants (1)
-
brett.cannon