Index: . =================================================================== --- . (revision 0) +++ . (revision 0) @@ -0,0 +1,156 @@ +/* Creado en 29/08/2007 */ +package org.python.core; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author David Perez Carmona + * An importar that allows to use separate .py and .class directories, so that the source folder is kept clean. + *

Example of use: + *

+ *   PyList meta = Py.defaultSystemState.meta_path;
+ *   meta.add(new SeparateFolderImporter(new File(baseDir, "src").toString(), new File(baseDir, "compiled").toString());
+ *   PythonInterpreter py = new PythonInterpreter();
+ *   py.exec(....);
+ * 
+ *

With this scenary: + * import mymodule.mysubmodule will scan for baseDir/src/mymodule/mysubmodule.py + * and will compile to baseDir/compiled/mymodule/mysubmodule$py.class. + */ +public class SeparateFolderImporter extends PyObject { + /**Directory where source .py files are found*/ + private String srcDir; + /**Directory where class files are placed*/ + private String classDir; + + /** + * Allows to separate source and class files in separate directories. + * @param srcDir Source directory that contains .py files + * @param classDir Class directory that contains .class files generated from the srcDir directory. + */ + public SeparateFolderImporter(String srcDir, String classDir) { + this.srcDir = srcDir; + this.classDir = classDir; + } + /**Data used by the loader.*/ + private static class LoaderData { + boolean useSource, pkg; + File srcDirPkg, + classDirPkg, + sourceFile, + compiledFile; + } + /** + * 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) { + String sourceName = "__init__.py"; + String compiledName = "__init__$py.class"; + String subdir = name.replace('.', File.separatorChar); + + // First check for packages + final LoaderData d = new LoaderData(); + d.srcDirPkg = new File(srcDir, subdir); + d.classDirPkg = new File(classDir, subdir); + d.sourceFile = new File(d.srcDirPkg, sourceName); + d.compiledFile = new File(d.classDirPkg, compiledName); + + d.pkg = d.srcDirPkg.isDirectory() && caseok(d.srcDirPkg, subdir) && d.sourceFile.isFile() || + d.classDirPkg.isDirectory() && caseok(d.classDirPkg, subdir) && d.compiledFile.isFile(); + if (!d.pkg) { + Py.writeDebug("import", "trying source " + d.sourceFile.getPath()); + sourceName = subdir + ".py"; + compiledName = subdir + "$py.class"; + d.sourceFile = new File(srcDir, sourceName); + d.compiledFile = new File(classDir, compiledName); + } + d.useSource = true; + if (d.sourceFile.isFile() && caseok(d.sourceFile, sourceName)) { + if (d.compiledFile.isFile() && caseok(d.compiledFile, compiledName)) { + Py.writeDebug("import", "trying precompiled "+ d.compiledFile.getPath()); + long pyTime = d.sourceFile.lastModified(); + long classTime = d.compiledFile.lastModified(); + if (classTime >= pyTime) { + d.useSource = false; + } + } + } else { + // If no source, try loading precompiled + Py.writeDebug("import", "trying precompiled with no source" + d.compiledFile.getPath()); + if (d.compiledFile.isFile() && caseok(d.compiledFile, compiledName)) { + d.useSource = false; + } else { + // No source, no precompiled available + return Py.None; + } + } + return new PyObject() { + /** + * 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 name) { + PyModule m = null; + if (d.pkg) { + m = imp.addModule(name); + PyObject filename = new PyString(d.srcDirPkg.getPath()); + m.__dict__.__setitem__("__path__", new PyList( + new PyObject[] { filename })); + m.__dict__.__setitem__("__file__", filename); + if (d.useSource) { + d.classDirPkg.mkdirs(); + } + } else { + if (d.useSource) { + d.compiledFile.getParentFile().mkdirs(); + } + } + PyObject o = d.useSource ? + imp.createFromSource(name, makeStream(d.sourceFile), + d.sourceFile.getAbsolutePath(), d.compiledFile.toString()): + imp.createFromPyClass(name, makeStream(d.compiledFile), + true, d.compiledFile.getAbsolutePath()); + return d.pkg ? m:o; + } + }; + } + /** + * 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) { + return find_module(name); + } + /**Code copied from {@link imp#makeStream(File)}.*/ + private static InputStream makeStream(File file) { + try { + return new FileInputStream(file); + } catch (IOException ioe) { + throw Py.IOError(ioe); + } + } + /**Code copied from {@link imp#caseok(File, String, int)}.*/ + static boolean caseok(File file, String lastPart) { + if (Options.caseok) { + return true; + } + try { + return new File(file.getCanonicalPath()).toString().endsWith(lastPart); + } catch (IOException exc) { + return false; + } + } +}