package org.python.core; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; /** * Load python source from jar or zip files. */ public class ZipFileImporter extends PyObject { private SyspathArchive archive; private String pathToArchive; /** * If this path is not an archive (.zip or .jar) then raise an ImportError, * otherwise this instance will handle this path. * * @param path the path to check for modules */ public ZipFileImporter(PyObject path) { if (!(path instanceof SyspathArchive)) { throw Py.ImportError(path.toString()); } this.archive = (SyspathArchive) path; String archiveName = SyspathArchive.getArchiveName(archive.string); this.pathToArchive = new File(archiveName).getAbsolutePath() + File.separatorChar; } /** * 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 */ public PyObject find_module(String name) { return find_module(name, Py.None); } /** * 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 * otherwise */ public PyObject find_module(String name, PyObject path) { ZipModuleInfo zip = getModuleInfo(name, this.archive); return (zip == null) ? Py.None : new ZipFileLoader(zip); } /** * Returns a string representation of the object. * * @return a string representation of the object. */ public String toString() { return this.getType().toString(); } /** * Returns the last part of a fully qualified name. For example, the name * <p> * <code> * a.b.c * </code> * </p> * would return <code>c</code>. * * @param name a fully qualified name * @return the last part of a fully qualified name */ private String getSubName(String name) { int x = name.lastIndexOf("."); if (x >= 0) { return name.substring(x + 1); } return name; } /** * Find the module name starting at the zipArchive root. This method will * look for both package and non-package names in the archive. If the name * is not found null will be returned. * * @param name the fully qualified module name * @param zipArchive the root of the path to begin looking * @return null if the module is not found, a ZipModuleInfo instance * otherwise */ private ZipModuleInfo getModuleInfo(String name, SyspathArchive zipArchive) { String entryName = getSubName(name); String sourceName = entryName + "/__init__.py"; String compiledName = entryName + "/__init__$py.class"; ZipEntry sourceEntry = zipArchive.getEntry(sourceName); ZipEntry compiledEntry = zipArchive.getEntry(compiledName); boolean pkg = (sourceEntry != null || compiledEntry != null); if (!pkg) { sourceName = entryName + ".py"; compiledName = entryName + "$py.class"; sourceEntry = zipArchive.getEntry(sourceName); compiledEntry = zipArchive.getEntry(compiledName); } else { zipArchive = zipArchive.makeSubfolder(entryName); } ZipModuleInfo info = null; if (sourceEntry != null) { Py.writeDebug("import", "trying source entry: " + sourceName + " from jar/zip file " + zipArchive); if (compiledEntry != null) { Py.writeDebug("import", "trying precompiled entry " + compiledName + " from jar/zip file " + zipArchive); long pyTime = sourceEntry.getTime(); long classTime = compiledEntry.getTime(); if (classTime >= pyTime) { info = new ZipModuleInfo(zipArchive, compiledEntry, true); } } if (info == null) { info = new ZipModuleInfo(zipArchive, sourceEntry, false); } } if (pkg && info != null) { info.path = new PyList(new PyObject[] { zipArchive }); } return info; } /** * Loader for zipfile python sources. */ public class ZipFileLoader extends PyObject { private ZipModuleInfo _info; public ZipFileLoader(ZipModuleInfo info) { this._info = info; } /** * A loaded module for the fully qualified name. * * @param moduleName the fully qualified name * @return a loaded module (added to sys.path) */ public PyObject load_module(String moduleName) { PyModule m = null; if (this._info.path != null) { m = imp.addModule(moduleName); m.__dict__.__setitem__("__path__", this._info.path); m.__dict__.__setitem__("__loader__", this); } byte[] is = null; // should this be closed? ZipEntry entry = this._info.zipEntry; try { is = this._info.archive.getInputStream(entry); } catch (IOException e) { Py.writeDebug("import", "loadFromZipFile exception: " + e.toString()); throw Py.ImportError("error loading from zipfile"); } String pathToEntry = pathToArchive + entry.getName(); PyObject o; if (this._info.compiled) { o = imp.createFromPyClass(moduleName, is, true, pathToEntry); } else { o = imp.createFromSource(moduleName, is, pathToEntry, null); } return (m == null) ? o : m; } /** * Returns a string representation of the object. * * @return a string representation of the object. */ public String toString() { return this.getType().toString(); } } private class ZipModuleInfo { /** The path of the package if it is a package. */ public PyObject path; /** Whether the code is already compiled. */ public boolean compiled; /** The zip entry for the file to load. */ public ZipEntry zipEntry; /** The archive in which the zip entry resides. */ public SyspathArchive archive; public ZipModuleInfo(SyspathArchive archive, ZipEntry zipEntry, boolean compiled) { this(archive, zipEntry, compiled, null); } public ZipModuleInfo(SyspathArchive archive, ZipEntry zipEntry, boolean compiled, PyObject path) { this.path = path; this.archive = archive; this.zipEntry = zipEntry; this.compiled = compiled; } } }