From: <cg...@us...> - 2008-12-30 08:17:09
|
Revision: 5816 http://jython.svn.sourceforge.net/jython/?rev=5816&view=rev Author: cgroves Date: 2008-12-30 08:17:05 +0000 (Tue, 30 Dec 2008) Log Message: ----------- Break the suffix ordering and module creation parts out of zipimporter into a new class, importer, and use that as the base class of a PEP-302 hook to import py and $py.class files off the classpath, ClasspathPyImporter. Like the JavaImporter, it triggers off a special entry in sys.path -- __pyclasspath__ in this case. Anything past __pyclasspath__/ in the sys.path entry is treated as a prefix, and modules are loaded below that. Add a default __pyclasspath__/ entry to sys.path to get py files directly on the classpath. This fixes issue1181, issue1747126 and possibly issue1108. Modified Paths: -------------- trunk/jython/CoreExposed.includes trunk/jython/Lib/site.py trunk/jython/src/org/python/core/JavaImporter.java trunk/jython/src/org/python/core/PySystemState.java trunk/jython/src/org/python/modules/imp.java trunk/jython/src/org/python/modules/zipimport/zipimport.java trunk/jython/src/org/python/modules/zipimport/zipimporter.java Added Paths: ----------- trunk/jython/Lib/test/classimport.jar trunk/jython/Lib/test/classimport_Lib.jar trunk/jython/Lib/test/test_classpathimporter.py trunk/jython/src/org/python/core/ClasspathPyImporter.java trunk/jython/src/org/python/core/util/importer.java Modified: trunk/jython/CoreExposed.includes =================================================================== --- trunk/jython/CoreExposed.includes 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/CoreExposed.includes 2008-12-30 08:17:05 UTC (rev 5816) @@ -1,4 +1,5 @@ org/python/core/AstList.class +org/python/core/ClasspathPyImporter.class org/python/core/PyArray.class org/python/core/PyBaseString.class org/python/core/PyBaseException.class Modified: trunk/jython/Lib/site.py =================================================================== --- trunk/jython/Lib/site.py 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/Lib/site.py 2008-12-30 08:17:05 UTC (rev 5816) @@ -65,7 +65,7 @@ def makepath(*paths): dir = os.path.join(*paths) - if dir == '__classpath__': + if dir == '__classpath__' or dir.startswith('__pyclasspath__'): return dir, dir dir = os.path.abspath(dir) return dir, os.path.normcase(dir) Added: trunk/jython/Lib/test/classimport.jar =================================================================== (Binary files differ) Property changes on: trunk/jython/Lib/test/classimport.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/jython/Lib/test/classimport_Lib.jar =================================================================== (Binary files differ) Property changes on: trunk/jython/Lib/test/classimport_Lib.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/jython/Lib/test/test_classpathimporter.py =================================================================== --- trunk/jython/Lib/test/test_classpathimporter.py (rev 0) +++ trunk/jython/Lib/test/test_classpathimporter.py 2008-12-30 08:17:05 UTC (rev 5816) @@ -0,0 +1,45 @@ +import unittest +import sys +from test import test_support +from java.io import File +from java.lang import Thread +from java.net import URLClassLoader + +class ClasspathImporterTestCase(unittest.TestCase): + def setUp(self): + self.orig_context = Thread.currentThread().contextClassLoader + self.orig_path = sys.path + + def tearDown(self): + Thread.currentThread().contextClassLoader = self.orig_context + sys.path = self.orig_path + try: + del sys.modules['flat_in_jar'] + del sys.modules['jar_pkg'] + del sys.modules['jar_pkg.prefer_compiled'] + except KeyError: + pass + + def setClassLoaderAndCheck(self, jar): + url = File(test_support.findfile(jar)).toURL() + Thread.currentThread().contextClassLoader = URLClassLoader([url]) + import flat_in_jar + self.assertEquals(flat_in_jar.value, 7) + import jar_pkg + from jar_pkg import prefer_compiled + self.assert_(prefer_compiled.compiled) + self.assertRaises(NameError, __import__, 'flat_bad') + self.assertRaises(NameError, __import__, 'jar_pkg.bad') + + def test_default_pyclasspath(self): + self.setClassLoaderAndCheck("classimport.jar") + + def test_path_in_pyclasspath(self): + sys.path = ['__pyclasspath__/Lib'] + self.setClassLoaderAndCheck("classimport_Lib.jar") + +def test_main(): + test_support.run_unittest(ClasspathImporterTestCase) + +if __name__ == '__main__': + test_main() Added: trunk/jython/src/org/python/core/ClasspathPyImporter.java =================================================================== --- trunk/jython/src/org/python/core/ClasspathPyImporter.java (rev 0) +++ trunk/jython/src/org/python/core/ClasspathPyImporter.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -0,0 +1,125 @@ +package org.python.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import org.python.core.util.importer; +import org.python.expose.ExposedMethod; +import org.python.expose.ExposedNew; +import org.python.expose.ExposedType; +import org.python.util.Generic; + +@ExposedType(name="ClasspathPyImporter") +public class ClasspathPyImporter extends importer<String> { + + public static final String PYCLASSPATH_PREFIX = "__pyclasspath__/"; + public static final PyType TYPE = PyType.fromClass(ClasspathPyImporter.class); + + public ClasspathPyImporter(PyType subType) { + super(subType); + } + + public ClasspathPyImporter() { + super(); + } + + @ExposedNew + @ExposedMethod + final void ClasspathPyImporter___init__(PyObject[] args, String[] kwds) { + ArgParser ap = new ArgParser("__init__", args, kwds, new String[] {"path"}); + String path = ap.getString(0); + if (path == null || !path.startsWith(PYCLASSPATH_PREFIX)) { + throw Py.ImportError("path isn't for classpath importer"); + } + if (!path.endsWith("/")) { + path += "/"; + } + this.path = path; + } + + /** + * Find the module for the fully qualified name. + * + * @param fullname the fully qualified name of the module + * @param path if not installed on the meta-path None or a module path + * @return a loader instance if this importer can load the module, None + * otherwise + */ + @ExposedMethod(defaults = "null") + final PyObject ClasspathPyImporter_find_module(String fullname, String path) { + return importer_find_module(fullname, path); + } + + /** + * Load a module for the fully qualified name. + * + * @param fullname the fully qualified name of the module + * @return a loaded PyModule + */ + @ExposedMethod + final PyObject ClasspathPyImporter_load_module(String fullname) { + return importer_load_module(fullname); + } + + @Override + protected boolean isAcceptableBytecode(String searchPath, String entry) { + return true; + } + + @Override + protected Bundle makeBundle(String fullFilename, String entry) { + InputStream is = entries.remove(entry); + return new Bundle(is) { + @Override + public void close() { + try { + inputStream.close(); + } catch (IOException e) { + throw Py.JavaError(e); + } + } + }; + } + + @Override + protected String makeEntry(String fullFilename) { + if (entries.containsKey(fullFilename)) { + return fullFilename; + } + InputStream is = null; + ClassLoader classLoader = Py.getSystemState().getClassLoader(); + if (classLoader != null) { + Py.writeDebug("import", "trying " + fullFilename + " in sys class loader"); + is = classLoader.getResourceAsStream(fullFilename); + } + if (is == null) { + Py.writeDebug("import", "trying " + fullFilename + " in context class loader"); + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fullFilename); + } + if (is != null) { + entries.put(fullFilename, is); + return fullFilename; + } + return null; + } + + @Override + protected String makeFilename(String fullname) { + return path.replace(PYCLASSPATH_PREFIX, "") + fullname.replace('.', '/'); + } + + @Override + protected String makePkgPath(String fullname) { + return path; + } + + @Override + protected String getSeparator() { + return "/"; + } + + private Map<String, InputStream> entries = Generic.map(); + + private String path; +} Modified: trunk/jython/src/org/python/core/JavaImporter.java =================================================================== --- trunk/jython/src/org/python/core/JavaImporter.java 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/src/org/python/core/JavaImporter.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -5,12 +5,10 @@ */ public class JavaImporter extends PyObject { - public JavaImporter() { - super(); - } - + public static final String JAVA_IMPORT_PATH_ENTRY = "__classpath__"; + public PyObject __call__(PyObject args[], String keywords[]) { - if(args[0].toString().endsWith("__classpath__")){ + if(args[0].toString().endsWith(JAVA_IMPORT_PATH_ENTRY)){ return this; } throw Py.ImportError("unable to handle"); @@ -18,7 +16,7 @@ /** * Find the module for the fully qualified name. - * + * * @param name the fully qualified name of the module * @return a loader instance if this importer can load the module, None * otherwise @@ -29,7 +27,7 @@ /** * Find the module for the fully qualified name. - * + * * @param name the fully qualified name of the module * @param path if installed on the meta-path None or a module path * @return a loader instance if this importer can load the module, None @@ -52,7 +50,7 @@ /** * Returns a string representation of the object. - * + * * @return a string representation of the object. */ public String toString() { Modified: trunk/jython/src/org/python/core/PySystemState.java =================================================================== --- trunk/jython/src/org/python/core/PySystemState.java 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/src/org/python/core/PySystemState.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -93,7 +93,7 @@ public static final PyString byteorder = new PyString("big"); public static final int maxint = Integer.MAX_VALUE; public static final int minint = Integer.MIN_VALUE; - + private static boolean initialized = false; /** The arguments passed to this program on the command line. */ @@ -101,7 +101,7 @@ public PyObject modules; public PyList path; - + // shadowed statics - don't use directly public static PyList warnoptions = new PyList(); public static PyObject builtins; @@ -112,7 +112,7 @@ public PyObject ps1 = new PyString(">>> "); public PyObject ps2 = new PyString("... "); - + public PyObject executable; private String currentWorkingDir; @@ -142,13 +142,15 @@ argv = (PyList)defaultArgv.repeat(1); path = (PyList)defaultPath.repeat(1); - path.append(Py.newString("__classpath__")); + path.append(Py.newString(JavaImporter.JAVA_IMPORT_PATH_ENTRY)); + path.append(Py.newString(ClasspathPyImporter.PYCLASSPATH_PREFIX)); executable = defaultExecutable; meta_path = new PyList(); path_hooks = new PyList(); path_hooks.append(new JavaImporter()); path_hooks.append(zipimporter.TYPE); + path_hooks.append(ClasspathPyImporter.TYPE); path_importer_cache = new PyDictionary(); currentWorkingDir = new File("").getAbsolutePath(); @@ -240,7 +242,7 @@ return shadowing.builtins; } } - + public synchronized void setBuiltins(PyObject value) { if (shadowing == null) { builtins = value; Added: trunk/jython/src/org/python/core/util/importer.java =================================================================== --- trunk/jython/src/org/python/core/util/importer.java (rev 0) +++ trunk/jython/src/org/python/core/util/importer.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -0,0 +1,230 @@ +package org.python.core.util; + +import java.io.InputStream; +import java.util.EnumSet; +import org.python.core.BytecodeLoader; +import org.python.core.Py; +import org.python.core.PyCode; +import org.python.core.PyList; +import org.python.core.PyModule; +import org.python.core.PyObject; +import org.python.core.PyType; +import org.python.core.imp; + +/** + * A base class for PEP-302 path hooks. Handles looking through source, compiled, package and module + * items in the right order, and creating and filling in modules. + */ +public abstract class importer<T> extends PyObject { + + static enum EntryType { + IS_SOURCE, IS_BYTECODE, IS_PACKAGE + }; + /** SearchOrder defines how we search for a module. */ + final SearchOrderEntry[] searchOrder; + + /** Module information */ + protected static enum ModuleInfo { + ERROR, NOT_FOUND, MODULE, PACKAGE + }; + + public importer(PyType subType) { + super(subType); + searchOrder = makeSearchOrder(); + } + + public importer() { + searchOrder = makeSearchOrder(); + } + + /** + * Returns the separator between directories and files used by this type of importer. + */ + protected abstract String getSeparator(); + + /** + * Returns the value to fill in __path__ on a module with the given full module name created by + * this importer. + */ + protected abstract String makePkgPath(String fullname); + + /** + * Given a full module name, return the potential file path in the archive (without extension). + */ + protected abstract String makeFilename(String fullname); + + /** + * Returns an entry for a filename from makeFilename with a potential suffix such that this + * importer can make a bundle with it, or null if fullFilename doesn't exist in this importer. + */ + protected abstract T makeEntry(String filenameAndSuffix); + + /** + * Returns a Bundle for fullFilename and entry, the result from a makeEntry call for + * fullFilename. + */ + protected abstract Bundle makeBundle(String filenameAndSuffix, T entry); + + private SearchOrderEntry[] makeSearchOrder(){ + return new SearchOrderEntry[] { + new SearchOrderEntry(getSeparator() + "__init__$py.class", + EnumSet.of(EntryType.IS_PACKAGE, EntryType.IS_BYTECODE)), + new SearchOrderEntry(getSeparator() + "__init__.py", + EnumSet.of(EntryType.IS_PACKAGE, EntryType.IS_SOURCE)), + new SearchOrderEntry("$py.class", EnumSet.of(EntryType.IS_BYTECODE)), + new SearchOrderEntry(".py", EnumSet.of(EntryType.IS_SOURCE)),}; + } + + protected final PyObject importer_find_module(String fullname, String path) { + ModuleInfo moduleInfo = getModuleInfo(fullname); + if (moduleInfo == ModuleInfo.ERROR || moduleInfo == ModuleInfo.NOT_FOUND) { + return Py.None; + } + return this; + } + + protected final PyObject importer_load_module(String fullname) { + ModuleCodeData moduleCodeData = getModuleCode(fullname); + if (moduleCodeData == null) { + return Py.None; + } + // the module *must* be in sys.modules before the loader executes the module code; the + // module code may (directly or indirectly) import itself + PyModule mod = imp.addModule(fullname); + mod.__dict__.__setitem__("__loader__", this); + if (moduleCodeData.ispackage) { + // add __path__ to the module *before* the code gets executed + PyList pkgpath = new PyList(); + pkgpath.add(makePkgPath(fullname)); + mod.__dict__.__setitem__("__path__", pkgpath); + } + imp.createFromCode(fullname, moduleCodeData.code, moduleCodeData.path); + Py.writeDebug("import", "import " + fullname + " # loaded from " + moduleCodeData.path); + return mod; + } + + /** + * Bundle is an InputStream, bundled together with a method that can close the input stream and + * whatever resources are associated with it when the resource is imported. + */ + protected abstract static class Bundle { + public InputStream inputStream; + + public Bundle(InputStream inputStream) { + this.inputStream = inputStream; + } + + /** + * Close the underlying resource if necessary. Raises an IOError if a problem occurs. + */ + public abstract void close(); + } + + protected abstract boolean isAcceptableBytecode(String searchPath, T entry); + + /** + * Return module information for the module with the fully qualified name. + * + * @param fullname + * the fully qualified name of the module + * @return the module's information + */ + protected final ModuleInfo getModuleInfo(String fullname) { + String path = makeFilename(fullname); + + for (SearchOrderEntry entry : searchOrder) { + T importEntry = makeEntry(path + entry.suffix); + if (importEntry == null) { + continue; + } + + if (entry.type.contains(EntryType.IS_PACKAGE)) { + return ModuleInfo.PACKAGE; + } + return ModuleInfo.MODULE; + } + return ModuleInfo.NOT_FOUND; + } + + /** + * Return the code object and its associated data for the module with the fully qualified name. + * + * @param fullname + * the fully qualified name of the module + * @return the module's ModuleCodeData object + */ + protected final ModuleCodeData getModuleCode(String fullname) { + String path = makeFilename(fullname); + String fullPath = makePkgPath(fullname); + + if (path.length() < 0) { + return null; + } + + for (SearchOrderEntry entry : searchOrder) { + String suffix = entry.suffix; + String searchPath = path + suffix; + String fullSearchPath = fullPath + suffix; + + Py.writeDebug("import", "# trying " + searchPath); + T tocEntry = makeEntry(searchPath); + if (tocEntry == null) { + continue; + } + + boolean ispackage = entry.type.contains(EntryType.IS_PACKAGE); + boolean isbytecode = entry.type.contains(EntryType.IS_BYTECODE); + + if (isbytecode && !isAcceptableBytecode(searchPath, tocEntry)) { + continue; + } + + Bundle bundle = makeBundle(searchPath, tocEntry); + byte[] codeBytes; + if (isbytecode) { + codeBytes = imp.readCode(fullname, bundle.inputStream, true); + } else { + codeBytes = imp.compileSource(fullname, bundle.inputStream, fullSearchPath); + } + bundle.close(); + + if (codeBytes == null) { + // bad magic number or non-matching mtime in byte code, try next + continue; + } + + PyCode code = BytecodeLoader.makeCode(fullname + "$py", codeBytes, fullSearchPath); + return new ModuleCodeData(code, ispackage, fullSearchPath); + } + return null; + } + + /** + * Container for PyModule code - whether or not it's a package - and its path. + */ + protected class ModuleCodeData { + public PyCode code; + public boolean ispackage; + public String path; + + public ModuleCodeData(PyCode code, boolean ispackage, String path) { + this.code = code; + this.ispackage = ispackage; + this.path = path; + } + } + + /** + * A step in the module search order: the file suffix and its entry type. + */ + protected static class SearchOrderEntry { + public String suffix; + public EnumSet<EntryType> type; + + public SearchOrderEntry(String suffix, EnumSet<EntryType> type) { + this.suffix = suffix; + this.type = type; + } + } + +} Modified: trunk/jython/src/org/python/modules/imp.java =================================================================== --- trunk/jython/src/org/python/modules/imp.java 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/src/org/python/modules/imp.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -9,8 +9,6 @@ import org.python.core.PyString; import org.python.core.PySystemState; import org.python.core.PyTuple; -import org.python.core.PyInteger; - import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -144,8 +142,7 @@ public static PyObject load_source(String modname, String filename, PyObject file) { PyObject mod = Py.None; if (file == null) { - // XXX: This should load the accompanying byte code file - // instead, if it exists + // XXX: This should load the accompanying byte code file instead, if it exists file = new PyFile(filename, "r", 1024); } Object o = file.__tojava__(InputStream.class); @@ -202,8 +199,7 @@ String compiledName; switch (type) { case PY_SOURCE: - // XXX: This should load the accompanying byte - // code file instead, if it exists + // XXX: This should load the accompanying byte code file instead, if it exists String resolvedFilename = sys.getPath(filename.toString()); compiledName = org.python.core.imp.makeCompiledFilename(resolvedFilename); if (name.endsWith(".__init__")) { @@ -229,8 +225,7 @@ break; case PKG_DIRECTORY: PyModule m = org.python.core.imp.addModule(name); - m.__dict__.__setitem__("__path__", - new PyList(new PyObject[] { filename })); + m.__dict__.__setitem__("__path__", new PyList(new PyObject[] {filename})); m.__dict__.__setitem__("__file__", filename); ModuleInfo mi = findFromSource(name, filename.toString(), true, true); type = mi.type; Modified: trunk/jython/src/org/python/modules/zipimport/zipimport.java =================================================================== --- trunk/jython/src/org/python/modules/zipimport/zipimport.java 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/src/org/python/modules/zipimport/zipimport.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -8,8 +8,6 @@ import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyStringMap; -import org.python.core.PyType; -import org.python.core.exceptions; /** * This module adds the ability to import Python modules (*.py, Modified: trunk/jython/src/org/python/modules/zipimport/zipimporter.java =================================================================== --- trunk/jython/src/org/python/modules/zipimport/zipimporter.java 2008-12-30 07:11:59 UTC (rev 5815) +++ trunk/jython/src/org/python/modules/zipimport/zipimporter.java 2008-12-30 08:17:05 UTC (rev 5816) @@ -6,27 +6,22 @@ import java.io.InputStream; import java.util.Date; import java.util.Enumeration; -import java.util.EnumSet; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.python.core.ArgParser; -import org.python.core.BytecodeLoader; import org.python.core.Py; -import org.python.core.PyCode; import org.python.core.PyDictionary; import org.python.core.PyInteger; -import org.python.core.PyList; import org.python.core.PyLong; -import org.python.core.PyModule; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PySystemState; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.core.imp; import org.python.core.util.FileUtil; import org.python.core.util.StringUtil; +import org.python.core.util.importer; import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; @@ -37,11 +32,11 @@ * * @author Philip Jenvey */ -@ExposedType(name = "zipimport.zipimporter") -public class zipimporter extends PyObject { - +@ExposedType(name = "zipimport.zipimporter", base = PyObject.class) +public class zipimporter extends importer<PyObject> { + public static final PyType TYPE = PyType.fromClass(zipimporter.class); - + @ExposedGet public static final PyString __doc__ = new PyString( "zipimporter(archivepath) -> zipimporter object\n" + @@ -50,25 +45,6 @@ "a zipfile. ZipImportError is raised if 'archivepath' doesn't point to\n" + "a valid Zip archive."); - /** zipSearchOrder defines how we search for a module in the Zip - * archive */ - static enum EntryType { - IS_SOURCE, IS_BYTECODE, IS_PACKAGE - }; - static final SearchOrderEntry[] zipSearchOrder = new SearchOrderEntry[] { - new SearchOrderEntry(File.separator + "__init__$py.class", - EnumSet.of(EntryType.IS_PACKAGE, EntryType.IS_BYTECODE)), - new SearchOrderEntry(File.separator + "__init__.py", - EnumSet.of(EntryType.IS_PACKAGE, EntryType.IS_SOURCE)), - new SearchOrderEntry("$py.class", EnumSet.of(EntryType.IS_BYTECODE)), - new SearchOrderEntry(".py", EnumSet.of(EntryType.IS_SOURCE)), - }; - - /** Module information */ - static enum ModuleInfo { - ERROR, NOT_FOUND, MODULE, PACKAGE - }; - /** Pathname of the Zip archive */ @ExposedGet public String archive; @@ -131,15 +107,13 @@ prefix = pathFile.getName() + File.separator + prefix; pathFile = parentFile; } - if (archive != null) { files = zipimport._zip_directory_cache.__finditem__(archive); if (files == null) { files = readDirectory(archive); zipimport._zip_directory_cache.__setitem__(archive, files); } - } - else { + } else { throw zipimport.ZipImportError("not a Zip file"); } @@ -166,11 +140,7 @@ */ @ExposedMethod(defaults = "null") final PyObject zipimporter_find_module(String fullname, String path) { - ModuleInfo moduleInfo = getModuleInfo(fullname); - if (moduleInfo == ModuleInfo.ERROR || moduleInfo == ModuleInfo.NOT_FOUND) { - return Py.None; - } - return this; + return importer_find_module(fullname, path); } public PyObject load_module(String fullname) { @@ -185,30 +155,7 @@ */ @ExposedMethod final PyObject zipimporter_load_module(String fullname) { - ModuleCodeData moduleCodeData = getModuleCode(fullname); - if (moduleCodeData == null) { - return Py.None; - } - - // the module *must* be in sys.modules before the loader - // executes the module code; the module code may (directly or - // indirectly) import itself - PyModule mod = imp.addModule(fullname); - - mod.__dict__.__setitem__("__loader__", this); - if (moduleCodeData.ispackage) { - // add __path__ to the module *before* the code gets - // executed - String fullpath = archive + File.separator + prefix + getSubname(fullname); - PyList pkgpath = new PyList(); - pkgpath.add(fullpath); - mod.__dict__.__setitem__("__path__", pkgpath); - } - - imp.createFromCode(fullname, moduleCodeData.code, moduleCodeData.path); - Py.writeDebug("import", "import " + fullname + " # loaded from Zip " + - moduleCodeData.path); - return mod; + return importer_load_module(fullname); } public String get_data(String path) { @@ -234,7 +181,7 @@ throw Py.IOError(path); } - ZipBundle zipBundle = getDataStream(path); + Bundle zipBundle = makeBundle(path, tocEntry); byte[] data; try { data = FileUtil.readBytes(zipBundle.inputStream); @@ -309,7 +256,7 @@ throw zipimport.ZipImportError("can't find module '" + fullname + "'"); } - String path = makeFilename(prefix, getSubname(fullname)); + String path = makeFilename(fullname); if (moduleInfo == ModuleInfo.PACKAGE) { path += File.separator + "__init__.py"; } @@ -334,7 +281,7 @@ * @return a ZipBundle with an InputStream to the file's * uncompressed data */ - public ZipBundle getDataStream(String datapath) { + public ZipBundle makeBundle(String datapath, PyObject entry) { datapath = datapath.replace(File.separatorChar, '/'); ZipFile zipArchive; try { @@ -355,91 +302,16 @@ } /** - * Return module information for the module with the fully - * qualified name. + * Determine if the byte code at path with the specified toc entry has a modification time + * greater than its accompanying source code's. * - * @param fullname the fully qualified name of the module - * @return the module's information - */ - private ModuleInfo getModuleInfo(String fullname) { - String path = makeFilename(prefix, getSubname(fullname)); - - for (SearchOrderEntry entry : zipSearchOrder) { - PyObject tocEntry = files.__finditem__(path + entry.suffix); - if (tocEntry == null) - continue; - - if (entry.type.contains(EntryType.IS_PACKAGE)) { - return ModuleInfo.PACKAGE; - } - return ModuleInfo.MODULE; - } - return ModuleInfo.NOT_FOUND; - } - - /** - * Return the code object and its associated data for the module - * with the fully qualified name. - * - * @param fullname the fully qualified name of the module - * @return the module's ModuleCodeData object - */ - private ModuleCodeData getModuleCode(String fullname) { - String path = makeFilename(prefix, getSubname(fullname)); - - if (path.length() < 0) { - return null; - } - - for (SearchOrderEntry entry : zipSearchOrder) { - String suffix = entry.suffix; - String searchPath = path + suffix; - - Py.writeDebug("import", "# trying " + archive + File.separator + path); - PyObject tocEntry = files.__finditem__(searchPath); - if (tocEntry == null) { - continue; - } - - boolean ispackage = entry.type.contains(EntryType.IS_PACKAGE); - boolean isbytecode = entry.type.contains(EntryType.IS_BYTECODE); - - if (isbytecode && isOutdatedBytecode(searchPath, tocEntry)) { - continue; - } - - String pathToEntry = archive + File.separator + searchPath; - ZipBundle zipBundle = getDataStream(searchPath); - byte[] codeBytes; - if (isbytecode) { - codeBytes = imp.readCode(fullname, zipBundle.inputStream, true); - } - else { - codeBytes = imp.compileSource(fullname, zipBundle.inputStream, pathToEntry); - } - zipBundle.close(); - - if (codeBytes == null) { - // bad magic number or non-matching mtime in byte code, try next - continue; - } - - PyCode code = BytecodeLoader.makeCode(fullname + "$py", codeBytes, pathToEntry); - return new ModuleCodeData(code, ispackage, pathToEntry); - } - return null; - } - - /** - * Determine if the byte code at path with the specified toc entry - * has a modification time greater than its accompanying source - * code's. - * - * @param path a String path to the byte code - * @param tocEntry the byte code's PyObject toc entry + * @param path + * a String path to the byte code + * @param tocEntry + * the byte code's PyObject toc entry * @return boolean whether or not the byte code is older */ - private boolean isOutdatedBytecode(String path, PyObject tocEntry) { + protected boolean isAcceptableBytecode(String path, PyObject tocEntry) { String sourcePath = path.substring(0, path.length() - 9) + ".py"; PyObject sourceTocEntry = files.__finditem__(sourcePath); if (sourceTocEntry == null) { @@ -528,13 +400,41 @@ return files; } + protected String getSeparator() { + return File.separator; + } + /** + * Given a full module name, return the potential file path in the archive (without extension). + * + * @param prefix + * a String value + * @param name + * a String modulename value + * @return the file path String value + */ + protected String makeFilename(String fullname) { + return prefix + getSubname(fullname).replace('.', File.separatorChar); + } + + @Override + protected String makePkgPath(String fullname) { + return archive + File.separator + prefix + getSubname(fullname); + } + + @Override + protected PyObject makeEntry(String fullFilename) { + return files.__finditem__(fullFilename); + } + + /** * Return fullname.split(".")[-1]. * - * @param fullname a String value + * @param fullname + * a String value * @return a split(".")[-1] String value */ - private String getSubname(String fullname) { + protected String getSubname(String fullname) { int i = fullname.lastIndexOf("."); if (i >= 0) { return fullname.substring(i + 1); @@ -543,18 +443,6 @@ } /** - * Given a (sub)modulename, return the potential file path in the - * archive (without extension). - * - * @param prefix a String value - * @param name a String modulename value - * @return the file path String value - */ - private String makeFilename(String prefix, String name) { - return prefix + name.replace('.', File.separatorChar); - } - - /** * Convert a time in milliseconds since epoch to DOS date format * * @param time in milliseconds, a long value @@ -613,43 +501,21 @@ } /** - * Container for PyModule code, whether or not it's a package and - * its path. - * + * ZipBundle is a ZipFile and one of its InputStreams, bundled together so the ZipFile can be + * closed when finished with its InputStream. */ - private class ModuleCodeData { - PyCode code; - boolean ispackage; - String path; - - public ModuleCodeData(PyCode code, boolean ispackage, String path) { - this.code = code; - this.ispackage = ispackage; - this.path = path; - } - } - - /** - * ZipBundle is a ZipFile and one of its InputStreams, bundled - * together so the ZipFile can be closed when finished with its - * InputStream. - * - */ - private class ZipBundle { + private class ZipBundle extends Bundle { ZipFile zipFile; - InputStream inputStream; public ZipBundle(ZipFile zipFile, InputStream inputStream) { + super(inputStream); this.zipFile = zipFile; - this.inputStream = inputStream; } /** - * Close the ZipFile; implicitly closes all of its - * InputStreams. + * Close the ZipFile; implicitly closes all of its InputStreams. * * Raises an IOError if a problem occurred. - * */ public void close() { try { @@ -660,19 +526,4 @@ } } } - - /** - * A step in the module search order: the file suffix and its file - * type. - * - */ - protected static class SearchOrderEntry { - public String suffix; - public EnumSet<EntryType> type; - - public SearchOrderEntry(String suffix, EnumSet<EntryType> type) { - this.suffix = suffix; - this.type = type; - } - } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |