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]