[Python-checkins] r52286 - sandbox/trunk/import_in_py/importer.py

brett.cannon python-checkins at python.org
Wed Oct 11 22:35:09 CEST 2006


Author: brett.cannon
Date: Wed Oct 11 22:35:08 2006
New Revision: 52286

Modified:
   sandbox/trunk/import_in_py/importer.py
Log:
A sketch of how to handle regenerating bytecode.  It does not work right now
because the needed marshal functions are not exposed at the moment.  This is
mostly to get the basic flow in code so as to see what API changes might be
needed.


Modified: sandbox/trunk/import_in_py/importer.py
==============================================================================
--- sandbox/trunk/import_in_py/importer.py	(original)
+++ sandbox/trunk/import_in_py/importer.py	Wed Oct 11 22:35:08 2006
@@ -24,6 +24,12 @@
 Clarifications for PEP 302:
     * Raise ImportError when load_module() fails to load a module without
       raising an exception.
+      
+Differences from C implementation:
+    * Bytecode handler handles regenerating the source code rather than when
+      the source code is handled.  This puts the burden of regeneration of
+      bytecode where it belongs since the source never requires the bytecode to
+      exist to work properly.
 
 Possible Py3K improvements:
     * Have __import__ check for sys.modules entry to alleviate need for every
@@ -176,8 +182,6 @@
 
     """Handler for importing Python source modules."""
 
-    # XXX Does not generate a .pyc at the moment.
-
     handles = 'py'
 
     def handle_file(self, module, fullname, path, file_path):
@@ -195,7 +199,7 @@
 class PyBytecodeHandler(object):
 
     """Handler for importing .pyc/.pyo modules."""
-    
+
     # XXX Should probably add a handle_string() method to PySourceHandler() so
     # that if the .pyc is outdated it can easily use PySourceHandler to do the
     # import for it and then write out the new .pyc .
@@ -206,17 +210,69 @@
         return 'pyc' if __debug__ else 'pyo'
         
     handles = property(_handles)
+    
+    def find_source(self, bytecode_path):
+        """Return the path to the source file for the bytecode or None if it
+        was not found."""
+        # XXX Might be nicer to not hard-code this and instead work off of a handler.
+        source_path = bytecode_path[:-1]
+        return source_path if os.path.exists(source_path) else None
+    
+    def validate_magic(self, marshalled_magic):
+        """Return a boolean as to whether the marshalled magic number is good
+        or not."""
+        return True
+        # XXX Need Python/marshal.c:r_long() exposed.
+        magic_number = marshal.loads(marshalled_magic)
+        return True if magic_number == imp.get_magic() else False
+
+    def validate_timestamp(self, marshalled_timestamp, source_path):
+        """Return a boolean as to whether the timestamp was valid or not
+        compared to the source file."""
+        return True
+        # XXX Need Python/marshal.c:r_long() exposed. 
+        bytecode_timestamp = marshal.loads(marshalled_timestamp)
+        source_timestampe = os.stat(source_path).st_mtime
+        if source_timestamp >> 32:
+            raise OverflowError("modification time overflows a 4 byte field")
+        return True if source_timestamp <= bytecode_timestamp else False
+        
+    def regenerate_bytecode(self, source_path, bytecode_path):
+        """Regenerate the bytecode_path file from source_path and return the
+        code object created."""
+        raise NotImplementedError("need to be able to marshal longs directly")
+        # XXX Need to be expose Python/marshal.c:w_long()
+        marshalled_magic = marshal.loads(imp.get_magic())
+        timestamp = os.stat(source_path).st_mtime
+        marshalled_timestamp = marshal.loads(timestamp)
+        with open(source_path, 'rU') as source_file:
+            source_code = source_file.read()
+        code_object = compile(source_code, source_path, 'exec')
+        with open(bytecode_path, 'wb') as bytecode_file:
+            bytecode_file.write(marshalled_magic)
+            bytecode_file.write(marshalled_timestamp)
+            marshal.dump(code_object, bytecode_file)
+        return code_object
+        
+        
 
     def handle_file(self, module, fullname, path, file_path):
         """Import the Python bytecode file at 'path' and use it to initialize
         'module'."""
-        module.__file__ = file_path
-        module.__name__ = fullname
         with open(file_path, 'rb') as bytecode_file:
-            # XXX No verification of magic number.
             magic = bytecode_file.read(4)
-            # XXX No verification of timestamp.
-            mtime = bytecode_file.read(4)
+            timestamp = bytecode_file.read(4)
             compiled_code = marshal.load(bytecode_file)
+        source_path = self.find_source(file_path)
+        if source_path:
+            if (not self.validate_magic(magic) or
+                not self.validate_timestamp(timestamp, source_path)):
+                compiled_code = self.regenerate_bytecode(source_path,
+                                                            bytecode_path)
+        else:
+            if not self.validate_magic(magic):
+                raise ImportError("bad magic number")
         exec compiled_code in module.__dict__
+        module.__file__ = file_path
+        module.__name__ = fullname
         return module


More information about the Python-checkins mailing list