14

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

Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
user1340802
  • 1,157
  • 4
  • 17
  • 36
  • I don't know any better way to unpact part of the tuple on java side other than casting to PyTuple. Personally, I'd create a second function python side that does the unpacking on the return value of the real function. – Daniel Teply May 11 '12 at 11:24

2 Answers2

7

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
Daniel Teply
  • 1,974
  • 1
  • 13
  • 10
  • well i am actually trying this way, but now i get a _"java.lang.ClassCastException: org.python.core.PySingleton cannot be cast to MyInterfaceType"_ from these : _PyObject myObj = jyMyClass.__call__(); return (MyInterfaceType)myObj .__tojava__(MyInterfaceType.class);_ – user1340802 May 10 '12 at 16:11
  • I'm surprised, the object you got back from Jython should be a PyFunction. Would you mind posting the Jython code? – Daniel Teply May 10 '12 at 16:36
2

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.
Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
  • thank you for your reply, BTW from my first test i get a _"Cannot make a static reference to the non-static method myMethod(String, String, boolean) from the type MyInterface"_, and as static is not allowed in interface definition i do not know how to proceed... (without wrapping everything inside a Python class) – user1340802 May 10 '12 at 12:44
  • @user1340802: Well, please post your code. My example doesn't contain `myMethtod(String, String, boolean)` so I have no idea what you're attempting to do. The error implies that you're maybe trying to do something like `MyInterface.myMethod(...)` which of course doesn't work. – Joonas Pulakka May 10 '12 at 18:46
  • sorry, i have modified my code, and now i am not able to step back to the error that i used to have. But have added code to my question... – user1340802 May 11 '12 at 08:58