Jython, use only a method from Python from Java?
Asked 07 September, 2021
Viewed 2.5K times
  • 57
Votes

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...

2 Answer