Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jython, use only a method from Python from Java?

When reading and using this article it assumes that we have a full object definition with class and mapping (proxy) objects from python to java.

Is it possible to only import a method (not defined inside a class, but using internal python class) from a piece of code in python without wrapping it in a class definition (without using the factory paradigm described above).

I would have like to do some kind of from myPyFile import myMethod from java, and then use myMethod, directly from java (maybe as a static method ?) ? But if this is possible, I do not have found any clue of how doing that (the interface stuff described in the article may be still necessary to tell Java how to use myMethod ?)

Best regards.

EDIT : I am now dealing with Jython 2.5.2, so it may be version dependent and much more easier in future ?

EDIT : Below in reply to Daniel :

Here is a sample code, to reproduce the error I get, and also get a working example from your helpful reply!

(Well and add a little other question on the mapping back to Java objects from a yield -ed Python/Jython result)

(@Joonas, Sorry, I have modified my code, and now I am not able to step back to the error that I used to have)

import org.python.core.Py;
import org.python.core.PyList;
import org.python.core.PyTuple;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.util.PythonInterpreter;

interface MyInterface {
    public PyList getSomething(String content, String glue, boolean bool);
}
class MyFactory {

    @SuppressWarnings("static-access")
    public MyFactory() {
        String cmd = "from mymodule import MyClass";
        PythonInterpreter interpreter = new PythonInterpreter(null, new PySystemState());

        PySystemState sys = Py.getSystemState();
        sys.path.append(new PyString("C:/jython2.5.2/Lib"));

        interpreter.exec(cmd);
        jyObjClass = interpreter.get("MyClass");
    }

    public MyInterface createMe() {
        PyObject myObj = jyObjClass.__call__();
        return (MyInterface)myObj.__tojava__(MyInterface.class);
    }

    private PyObject jyObjClass;
}


public class Main {

    public static void main(String[] args) {

    /*
// with only :
    PythonInterpreter interpreter = new PythonInterpreter();

     i get : 
Exception in thread "main" Traceback (most recent call last):
  File "<string>", line 1, in <module>
LookupError: no codec search functions registered: can't find encoding 'iso8859_1'

which is probably due to the : 
#!/usr/bin/env python
# -*- coding: latin-1 -*-

// yes i am from France, so my - sorry for that - bad english ;) and have to deal with non 127 ascii chars :)
     */

    PythonInterpreter interpreter = new PythonInterpreter(null, new PySystemState());

    PySystemState sys = Py.getSystemState();
    sys.path.append(new PyString("C:/jython2.5.2/Lib"));

    interpreter.exec("from mymodule import getSomething"); 
    PyObject tmpFunction = interpreter.get("getSomething"); 
    System.err.println(tmpFunction.getClass()); 
    MyInterface i = (MyInterface) tmpFunction.__tojava__(MyInterface.class); 
    System.err.println(i.getSomething("test", " - ", true));
    for (Object x : i.getSomething("test", " - ", true)) {
        System.out.println(x);
        // How can i get back an equivallent of the Python _"for (a, b) in getSomething:"_ 
        // with _"a"_ being PyUnicode or better String, and _"b"_ being boolean ?
    }

    // ^^ so adapting Daniel Teply solution works ! Thanks to him... 
    // BTW the part below did not work : but i may have missed or/and also mixed many things :/
    // i feel Jython damned hard to dive in :/ 
    // and really hope that the sample that i post and answers that i get will help others to swim!

    try {
        MyFactory factory = new MyFactory();
        MyInterface myobj = factory.createMe();

        PyList myResult = myobj.getSomething("test", " - ", true);
        System.out.println(myResult);
    }
    catch (Exception e) {
        System.out.println(e);
        // produce a : java.lang.ClassCastException: org.python.core.PySingleton cannot be cast to MyInterface
        // EDIT : see below last edit, this error may be due to my forgotten heritage from interface in the python code!
    }

    System.exit(-1);
    }
}

Python part: (mymodule.py)

#!/usr/bin/env python
# -*- coding: latin-1 -*-

class MyClass:
    def __init__(selfself):
        pass
    def getSomething(self, content, glue = '', bool = True):
        for x in range(5):
            yield (glue.join(map(str, (content, x, chr(97 + x))))), bool
        #return list()

def getSomething(content, glue = '', bool = True):
    print "test"
    myclass = MyClass()
    return list(myclass.getSomething(content, glue, bool))

def main():
    test()

if __name__ == "__main__":
    main()

EDIT :

Partially answering myself, for the inner question (inside comments):
(actually I feel my answer and code are ugly, but it works and seems to be ok to un-tuple I do not know if there is a better Jythonic-way to do it, if so, I am really interested :))

for (Object x : i.getSomething("test", " - ", true)) {
    System.out.println(x);
    // How can i get back an equivallent of the Python _"for (a, b) in getSomething:"_ 
    // with _"a"_ being PyUnicode or better String, and _"b"_ being boolean ?

    // answering myself here :
    PyTuple mytuple = (PyTuple) x; // casting back x as PyTuple, can we have a java equivalent to _`(a, b) = x_ ? not sure...
    PyObject a = mytuple.__getitem__(0);
    PyObject b = mytuple.__getitem__(1);
    String aS = a.toString(); // mapping a unicode python string to java is as simple?
    boolean bB = b.toString().toLowerCase().equals("true");
    System.out.println(mytuple + "[" + aS + "][" + b + "][" + bB + "]");


EDIT:

Answering myself to the part about "produce a : "java.lang.ClassCastException: org.python.core.PySingleton cannot be cast to MyInterface"... most of my misunderstanding and errors where due to the fact that I had forgotten to handle the Java from the Python part! (see my code above, I leave it uncorrected about this fact because it was not my main question, and in its actual form, it's a working answer about this main question, many thanks to Daniel and Joonas who helped me to understand). So for the factory paradigm, one should NOT forget to add the adequate import to its Python file :

from testjython.interfaces import MyInterface #// defining method inside a MyInterface.java

class MyClass(MyInterface):
    [...]

One other difficulty that I had was to correctly handle the import and packages. BTW, adding this code to the upper code will produce a TypeError: can't convert to org.python.core.PyList but this is another concern...

like image 960
user1340802 Avatar asked May 10 '12 09:05

user1340802


People also ask

Can we call Python code from Java?

You can easily call python functions from Java code with Jython. That is as long as your python code itself runs under jython, i.e. doesn't use some c-extensions that aren't supported. If that works for you, it's certainly the simplest solution you can get.

How does Jython integrate with Java?

Download jython-standalone-2.7. 0. jar - For embedding Jython in Java applications from their official downloads page: http://www.jython.org/downloads.html and include this jar file in Java CLASSPATH environment variable.

Is Jython interpreter or compiler?

Just as Java, Jython program compiles to bytecode.


2 Answers

You can use PyObject.__call__(Object... args) to invoke any callable Python object. You can get the PyFunction representing your function from the java side the same way you example is getting the python employee class.

Alternativeley, you can hide this behind a single method interface on the java side by calling __tojava__(Interface.class) on the function you retrieved from the Python interpreter. Detailed example (actually tested!): python file:

def tmp():
    return "some text"

java:

public interface I{
    public String tmp();
}

public static void main(String[] args) {
    PythonInterpreter interpreter = new PythonInterpreter();
    interpreter.exec("from test import tmp");
    PyObject tmpFunction = interpreter.get("tmp");
    System.err.println(tmpFunction.getClass());
    I i = (I) x.__tojava__(I.class);
    System.err.println(i.tmp());

}

output:

class org.python.core.PyFunction
some text
like image 157
Daniel Teply Avatar answered Oct 01 '22 21:10

Daniel Teply


Importing only a method isn't possible because in Java, methods (or functions) aren't first-class objects, i.e. there's no way to refer to a method without first referring to some class (or interface). Even static methods are enclosed in a class and you refer to them via the class object.

However, you can get fairly close with the solution in introduced in Jython 2.5.2: Jython functions work directly as implementations of single abstract method Java interfaces (see http://www.zyasoft.com/pythoneering/2010/09/jython-2.5.2-beta-2-is-released/). So you can define an interface in Java - it's essential that it contains exactly one method definition:

interface MyInterface {
    int multiply(int x, int y);
}

Plus something like this in Jython:

myFunction = lambda x, y : x * y

and use that as a MyInterface in Java. You still have to use some sort of factory pattern, as described in the article you linked to, to get the Jython function to Java, but something like this works (probably contains errors, but the idea is):

PyObject myFunction = interpreter.get("myFunction"); 
MyInterface myInterface = (MyInterface)myFunction.__tojava__(MyInterface.class);
int x = myInterface.multiply(2, 3); // Should return 6.
like image 38
Joonas Pulakka Avatar answered Oct 01 '22 20:10

Joonas Pulakka