From 906e5222f38d94cd0b4bd3b200d77a221cc6e589 Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Sat, 31 Oct 2009 15:32:06 +0530 Subject: [PATCH] Added support for importing Python modules from paths other than Java Classpath. --- .classpath | 1 + .pydevproject | 5 +- {src/example/java => Lib}/person.py | 0 .../jywrapper/example/DependentTest.java | 12 +++++ .../jywrapper/example/EmployeeTest.java | 13 +++++ .../abhinavsarkar/jywrapper/JyWrapper.java | 52 +++++++++++++------ .../jywrapper/PyImportLoader.java | 8 +-- .../net/abhinavsarkar/jywrapper/Util.java | 32 ++++++++++++ .../jywrapper/messages.properties | 2 +- 9 files changed, 103 insertions(+), 22 deletions(-) rename {src/example/java => Lib}/person.py (100%) diff --git a/.classpath b/.classpath index 4445cfe..644c4a6 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,7 @@ + diff --git a/.pydevproject b/.pydevproject index f49da17..8f7f4d9 100644 --- a/.pydevproject +++ b/.pydevproject @@ -2,6 +2,9 @@ -python 2.6 +jython 2.5 Default + +/jywrapper/Lib + diff --git a/src/example/java/person.py b/Lib/person.py similarity index 100% rename from src/example/java/person.py rename to Lib/person.py diff --git a/src/example/java/net/abhinavsarkar/jywrapper/example/DependentTest.java b/src/example/java/net/abhinavsarkar/jywrapper/example/DependentTest.java index 900c747..73c7e92 100644 --- a/src/example/java/net/abhinavsarkar/jywrapper/example/DependentTest.java +++ b/src/example/java/net/abhinavsarkar/jywrapper/example/DependentTest.java @@ -8,6 +8,18 @@ import org.junit.Test; public class DependentTest { + private static final String PYLIB_DIR_NAME = "Lib"; + + static { + try { + JyWrapper.addToPythonPath( + DependentTest.class.getClassLoader() + .getResource(PYLIB_DIR_NAME).toURI()); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + private final Employee Employee_ = JyWrapper.wrap(Employee.class, "person.Employee"); private final Dependent Dependent_ = JyWrapper.wrap(Dependent.class, "person.Dependent"); diff --git a/src/example/java/net/abhinavsarkar/jywrapper/example/EmployeeTest.java b/src/example/java/net/abhinavsarkar/jywrapper/example/EmployeeTest.java index 23937f3..089ad08 100644 --- a/src/example/java/net/abhinavsarkar/jywrapper/example/EmployeeTest.java +++ b/src/example/java/net/abhinavsarkar/jywrapper/example/EmployeeTest.java @@ -3,12 +3,25 @@ package net.abhinavsarkar.jywrapper.example; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + import net.abhinavsarkar.jywrapper.JyWrapper; import org.junit.Test; public class EmployeeTest { + private static final String PYLIB_DIR_NAME = "Lib"; + + static { + try { + JyWrapper.addToPythonPath( + DependentTest.class.getClassLoader() + .getResource(PYLIB_DIR_NAME).toURI()); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + private final Employee Employee_ = JyWrapper.wrap(Employee.class, "person.Employee"); private final Dependent Dependent_ = JyWrapper.wrap(Dependent.class, "person.Dependent"); diff --git a/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java b/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java index 28f9e46..2151ee0 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/JyWrapper.java @@ -2,14 +2,17 @@ package net.abhinavsarkar.jywrapper; import static net.abhinavsarkar.jywrapper.Messages._; - +import java.net.URI; import net.abhinavsarkar.jywrapper.annotation.Wraps; import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; +import org.python.core.Py; import org.python.core.PyClass; import org.python.core.PyFunction; import org.python.core.PyModule; import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.core.PySystemState; import org.python.core.PyType; /** @@ -18,7 +21,7 @@ import org.python.core.PyType; * @param The type of the java class to wrap the Python class/module with. */ public final class JyWrapper { - + private JyWrapper() { } @@ -27,20 +30,20 @@ public final class JyWrapper { if (annotation == null) { throw new PythonImportNotFoundException(_("JyWrapper.7", javaClass)); //$NON-NLS-1$ } - + return wrap(javaClass, annotation.value()); } /** * @param pyImportName The full import name of the Python class/module * to wrap. - * @return An instance of {@link UninitedPyObjectWrapper}, ready to be + * @return An instance of {@link UninitedPyObjectWrapper}, ready to be * initialized. * @throws IllegalStateException Thrown if the java Class to be used to - * wrap the Python module/class, has not been supplied by earlier + * wrap the Python module/class, has not been supplied by earlier * calling {@link JyWrapper#with(Class)}. * @throws IllegalArgumentException Thrown if the pyImportName parameter - * is null. + * is null. */ public static T wrap(final Class javaClass, final String pyImportName) { if (javaClass == null) { @@ -49,24 +52,24 @@ public final class JyWrapper { if (pyImportName == null) { throw new IllegalArgumentException(_("JyWrapper.6", "pyImportName")); //$NON-NLS-1$ //$NON-NLS-2$ } - + final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); - if (!(pyImport instanceof PyType - || pyImport instanceof PyModule + if (!(pyImport instanceof PyType + || pyImport instanceof PyModule || pyImport instanceof PyClass)) { throw new IllegalArgumentException(_("JyWrapper.5", pyImportName)); //$NON-NLS-1$ } return Util.py2Java(pyImport, javaClass); } - + /** - * @param The return type of the {@link PyCallable} instance. + * @param The return type of the {@link PyCallable} instance. * @param pyImportName The full import name of the Python function to wrap. * @param returnType The class of the return type. * @return An instance of {@link PyCallable} which wraps the * Python function given in parameter. - * @throws IllegalArgumentException Thrown if the any of the parameters - * supplied are null or if the pyImportName parameter supplied does not + * @throws IllegalArgumentException Thrown if the any of the parameters + * supplied are null or if the pyImportName parameter supplied does not * correspond to a Python function. */ public static PyCallable wrapPyFunction( @@ -77,16 +80,33 @@ public final class JyWrapper { if (returnType == null) { throw new IllegalArgumentException(_("JyWrapper.6", "returnType")); //$NON-NLS-1$ //$NON-NLS-2$ } - + final PyObject pyImport = PyImportLoader.loadPyImport(pyImportName); if (!(pyImport instanceof PyFunction)) { throw new IllegalArgumentException(_("JyWrapper.0", pyImportName)); //$NON-NLS-1$ } - + @SuppressWarnings("unchecked") final PyCallable newInstance = PyObjectProxy.newInstance( pyImport, PyCallable.class); return newInstance; } - + + public static void addToPythonPath(final URI path) { + final PySystemState pySystemState = Py.getSystemState(); + final PyString resolvedPath = Util.resolvePath(path); + if (resolvedPath != null && !pySystemState.path.contains(resolvedPath)) { + pySystemState.path.append(resolvedPath); + } + } + + + public static void removeFromPythonPath(final URI path) { + final PySystemState pySystemState = Py.getSystemState(); + final PyString resolvedPath = Util.resolvePath(path); + if (resolvedPath != null && pySystemState.path.contains(resolvedPath)) { + pySystemState.path.remove(resolvedPath); + } + } + } diff --git a/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java b/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java index 1a9e05a..8533071 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/PyImportLoader.java @@ -9,7 +9,6 @@ import net.abhinavsarkar.jywrapper.exception.PythonImportNotFoundException; import org.python.core.Py; import org.python.core.PyException; import org.python.core.PyObject; -import org.python.core.PySystemState; /** * @author AbhinavSarkar @@ -17,8 +16,8 @@ import org.python.core.PySystemState; */ public final class PyImportLoader { - private static final PyObject importer = new PySystemState().getBuiltins() - .__getitem__(Py.newString("__import__")); //$NON-NLS-1$ + private static final PyObject importer = + Py.getSystemState().getBuiltins().__getitem__(Py.newString("__import__")); private static final ConcurrentHashMap loadedPyImports = new ConcurrentHashMap(); @@ -35,7 +34,7 @@ public final class PyImportLoader { throws PythonImportNotFoundException { if (!loadedPyImports.containsKey(fullImportName)) { final int i = fullImportName.lastIndexOf('.'); - final String errorMsg = _("PyImportLoader.1", fullImportName); //$NON-NLS-1$ + final String errorMsg = _("PyImportLoader.1", fullImportName, Py.getSystemState().path); //$NON-NLS-1$ PyObject pyImport; if (i == -1) { final String pyModuleName = fullImportName; @@ -59,4 +58,5 @@ public final class PyImportLoader { } return loadedPyImports.get(fullImportName); } + } diff --git a/src/main/java/net/abhinavsarkar/jywrapper/Util.java b/src/main/java/net/abhinavsarkar/jywrapper/Util.java index 220659a..6379834 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/Util.java +++ b/src/main/java/net/abhinavsarkar/jywrapper/Util.java @@ -2,6 +2,10 @@ package net.abhinavsarkar.jywrapper; import static net.abhinavsarkar.jywrapper.Messages._; +import java.io.File; +import java.io.IOException; +import java.net.URI; + import org.python.core.Py; import org.python.core.PyClass; import org.python.core.PyFunction; @@ -83,4 +87,32 @@ final class Util { return word.replaceAll("([A-Z])", "_$1").toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$ } + static PyString resolvePath(final URI pythonPath) { + PyString resolvedPath = null; + if (pythonPath.getScheme().equals("jar")) { + final String schemeSpecificPart = pythonPath.getSchemeSpecificPart(); + final int lastIndexOf = schemeSpecificPart.lastIndexOf("!"); + final String jarPath = schemeSpecificPart.substring(5, lastIndexOf); + final File file = new File(jarPath); + if (file.exists()) { + try { + resolvedPath = Py.newString(file.getCanonicalPath() + + schemeSpecificPart.substring(lastIndexOf + 1)); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + } else if (pythonPath.getScheme().equals("file")) { + final File file = new File(pythonPath); + if (file.exists()) { + try { + resolvedPath = Py.newString(file.getCanonicalPath()); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + } + return resolvedPath; + } + } diff --git a/src/main/java/net/abhinavsarkar/jywrapper/messages.properties b/src/main/java/net/abhinavsarkar/jywrapper/messages.properties index 559e274..25ac07c 100644 --- a/src/main/java/net/abhinavsarkar/jywrapper/messages.properties +++ b/src/main/java/net/abhinavsarkar/jywrapper/messages.properties @@ -14,4 +14,4 @@ JyWrapper.12=Trying to get instance attribute "{0}" from a Python Type JyWrapper.13=Can''t set attribute: {0} JyWrapper.14=Trying to set instance attribute "{0}" in a python class JyWrapper.15=No {0} named "{1}" in the backing Python Type {2} -PyImportLoader.1=Python Import "{0}" not found in pythonpath +PyImportLoader.1=Python Import "{0}" not found in pythonpath {1}