[Python-checkins] r52553 - 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
Mon Oct 30 03:21:22 CET 2006


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]


More information about the Python-checkins mailing list