[Jython-checkins] jython: Retain target file name, to be used in code objects if source not available

jim.baker jython-checkins at python.org
Wed Dec 24 22:17:16 CET 2014


https://hg.python.org/jython/rev/ae190b66bf4c
changeset:   7470:ae190b66bf4c
user:        Jim Baker <jim.baker at rackspace.com>
date:        Wed Dec 24 14:17:00 2014 -0700
summary:
  Retain target file name, to be used in code objects if source not available

If the compiled (*.py$class) file is available without the companion
source file, code objects when loaded now set co_filename to the
target filename originally associated with that file, regardless of
what the name of the file is now. Note as with CPython, modules can be
compiled with a different target filename than their source filename.

Bumps bytecode magic (org.python.core.imp.APIVersion) to 35. Adds a
new retained annotation, org.python.compiler.Filename, to annotate
compiled modules, as implemented using PyFunctionTable.

This change can be seen in the following decompilation of a compiled
Python module:

import org.python.compiler.*;
import org.python.core.*;

@APIVersion(35)
@MTime(1419447569000L)
@Filename("foo.py")
public class foo$py extends PyFunctionTable implements PyRunnable
{  ... }

Fixes the last failing test case in test_import, test_module_without_source

files:
  src/org/python/compiler/ClassFile.java    |   5 +-
  src/org/python/compiler/Filename.java     |   9 +
  src/org/python/core/AnnotationReader.java |  14 ++-
  src/org/python/core/imp.java              |  63 +++++++++-
  4 files changed, 78 insertions(+), 13 deletions(-)


diff --git a/src/org/python/compiler/ClassFile.java b/src/org/python/compiler/ClassFile.java
--- a/src/org/python/compiler/ClassFile.java
+++ b/src/org/python/compiler/ClassFile.java
@@ -189,7 +189,7 @@
             av.visitEnd();
         } 
     }
-    
+
     public void write(OutputStream stream) throws IOException {
         cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, this.name, null, this.superclass, interfaces);
         AnnotationVisitor av = cw.visitAnnotation("Lorg/python/compiler/APIVersion;", true);
@@ -203,6 +203,9 @@
         av.visitEnd();
 
         if (sfilename != null) {
+            av = cw.visitAnnotation("Lorg/python/compiler/Filename;", true);
+            av.visit("value", sfilename);
+            av.visitEnd();
             cw.visitSource(sfilename, null);
         }
         endClassAnnotations();
diff --git a/src/org/python/compiler/Filename.java b/src/org/python/compiler/Filename.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/compiler/Filename.java
@@ -0,0 +1,9 @@
+package org.python.compiler;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface Filename {
+    String value();
+}
diff --git a/src/org/python/core/AnnotationReader.java b/src/org/python/core/AnnotationReader.java
--- a/src/org/python/core/AnnotationReader.java
+++ b/src/org/python/core/AnnotationReader.java
@@ -24,9 +24,11 @@
 
     private boolean nextVisitIsVersion = false;
     private boolean nextVisitIsMTime = false;
+    private boolean nextVisitIsFilename = false;
 
     private int version = -1;
     private long mtime = -1;
+    private String filename = null;
 
     /**
      * Reads the classfile bytecode in data and to extract the version.
@@ -50,6 +52,7 @@
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
         nextVisitIsVersion = desc.equals("Lorg/python/compiler/APIVersion;");
         nextVisitIsMTime = desc.equals("Lorg/python/compiler/MTime;");
+        nextVisitIsFilename = desc.equals("Lorg/python/compiler/Filename;");
         return new AnnotationVisitor(Opcodes.ASM4) {
 
         	public void visit(String name, Object value) {
@@ -58,8 +61,11 @@
         			nextVisitIsVersion = false;
         		} else if (nextVisitIsMTime) {
         			mtime = (Long)value;
-        			nextVisitIsVersion = false;
-        		}
+        			nextVisitIsMTime = false;
+        		} else if (nextVisitIsFilename) {
+                    filename = (String)value;
+                    nextVisitIsFilename = false;
+                }
         	}
 		};
     }
@@ -71,4 +77,8 @@
     public long getMTime() {
         return mtime;
     }
+
+    public String getFilename() {
+        return filename;
+    }
 }
diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java
--- a/src/org/python/core/imp.java
+++ b/src/org/python/core/imp.java
@@ -27,7 +27,7 @@
 
     private static final String UNKNOWN_SOURCEFILE = "<unknown>";
 
-    private static final int APIVersion = 34;
+    private static final int APIVersion = 35;
 
     public static final int NO_MTIME = -1;
 
@@ -35,6 +35,34 @@
     // imports unless `from __future__ import absolute_import`
     public static final int DEFAULT_LEVEL = -1;
 
+    public static class CodeData {
+        private final byte[] bytes;
+        private final long mtime;
+        private final String filename;
+
+        public CodeData(byte[] bytes, long mtime, String filename) {
+            this.bytes = bytes;
+            this.mtime = mtime;
+            this.filename = filename;
+        }
+
+        public byte[] getBytes() {
+            return bytes;
+        }
+
+        public long getMTime() {
+            return mtime;
+        }
+
+        public String getFilename() {
+            return filename;
+        }
+    }
+
+    public static enum CodeImport {
+        source, compiled_only;
+    }
+
     /** A non-empty fromlist for __import__'ing sub-modules. */
     private static final PyObject nonEmptyFromlist = new PyTuple(Py.newString("__doc__"));
 
@@ -174,9 +202,14 @@
 
     static PyObject createFromPyClass(String name, InputStream fp, boolean testing,
                                       String sourceName, String compiledName, long mtime) {
-        byte[] data = null;
+        return createFromPyClass(name, fp, testing, sourceName, compiledName, mtime, CodeImport.source);
+    }
+
+    static PyObject createFromPyClass(String name, InputStream fp, boolean testing,
+                                      String sourceName, String compiledName, long mtime, CodeImport source) {
+        CodeData data = null;
         try {
-            data = readCode(name, fp, testing, mtime);
+            data = readCodeData(name, fp, testing, mtime);
         } catch (IOException ioe) {
             if (!testing) {
                 throw Py.ImportError(ioe.getMessage() + "[name=" + name + ", source=" + sourceName
@@ -188,7 +221,8 @@
         }
         PyCode code;
         try {
-            code = BytecodeLoader.makeCode(name + "$py", data, sourceName);
+            code = BytecodeLoader.makeCode(name + "$py", data.getBytes(),
+                    source == CodeImport.compiled_only ? data.getFilename() : sourceName);
         } catch (Throwable t) {
             if (testing) {
                 return null;
@@ -199,7 +233,6 @@
 
         Py.writeComment(IMPORT_LOG, String.format("import %s # precompiled from %s", name,
                                                   compiledName));
-
         return createFromCode(name, code, compiledName);
     }
 
@@ -208,6 +241,14 @@
     }
 
     public static byte[] readCode(String name, InputStream fp, boolean testing, long mtime) throws IOException {
+        return readCodeData(name, fp, testing, mtime).getBytes();
+    }
+
+    public static CodeData readCodeData(String name, InputStream fp, boolean testing) throws IOException {
+        return readCodeData(name, fp, testing, NO_MTIME);
+    }
+
+    public static CodeData readCodeData(String name, InputStream fp, boolean testing, long mtime) throws IOException {
         byte[] data = readBytes(fp);
         int api;
         AnnotationReader ar = new AnnotationReader(data);
@@ -226,7 +267,7 @@
                 return null;
             }
         }
-        return data;
+        return new CodeData(data, mtime, ar.getFilename());
     }
 
     public static byte[] compileSource(String name, File file) {
@@ -582,8 +623,9 @@
                     Py.writeDebug(IMPORT_LOG, "trying precompiled " + compiledFile.getPath());
                     long classTime = compiledFile.lastModified();
                     if (classTime >= pyTime) {
-                        PyObject ret = createFromPyClass(modName, makeStream(compiledFile), true,
-                                                         displaySourceName, displayCompiledName, pyTime);
+                        PyObject ret = createFromPyClass(
+                                modName, makeStream(compiledFile), true,
+                                displaySourceName, displayCompiledName, pyTime);
                         if (ret != null) {
                             return ret;
                         }
@@ -598,8 +640,9 @@
             // If no source, try loading precompiled
             Py.writeDebug(IMPORT_LOG, "trying precompiled with no source " + compiledFile.getPath());
             if (compiledFile.isFile() && caseok(compiledFile, compiledName)) {
-                return createFromPyClass(modName, makeStream(compiledFile), true, displaySourceName,
-                                         displayCompiledName);
+                return createFromPyClass(
+                        modName, makeStream(compiledFile), true, displaySourceName,
+                        displayCompiledName, NO_MTIME, CodeImport.compiled_only);
             }
         } catch (SecurityException e) {
             // ok

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list