[Walter Moreira]
Personal site"Young man," said von Neumann, "in mathematics you don't understand things, you just get used to them."
--quoted in G. Zukav The Dancing Wu Li Masters.
Using Python from Java without anyone noticing
| Author: | Walter Moreira |
|---|---|
| Status: | (Nov 2009) Updated to work with Jython 2.5 |
Problem
We want to write classes in Python and to distribute them as Java classes, without the user knowing about Jython or Python.
We want to make the process of generating a Java class from Python code as straightforward as possible.
This was solved originally, in Jython 2.2 and previous versions, using the compiler jythonc. Unfortunately, such tool is now deprecated and a solution for Jython 2.5 is still in the works. The following solution aims to be a substitute until the successor of jythonc is released. It is inspired in the article Simple and Efficient Jython Object Factories
Solution
The jar file pyconnection.jar contains the full Jython distribution together with a thin wrapper which allows the following workflow.
Dan Developer is the person who writes the Python code and who wants to deliver a Java class. Ugo User is the person who uses the Java class delivered by Dan.
In this example, Dan delivers a sophisticated Java class which greets the user. The unsuspecting user will never know he is shaking hands with Python.
How Ugo uses what Dan delivers
Ugo needs:
- pyconnection.jar: download it here.
- greeter.jar: delivered by Dan Developer.
Dan delivers a file greeter.jar and promises an interface GreeterType as:
public interface GreeterType {
public void __init__(String name);
public String greet(String salutation);
}
Ugo drops greeter.jar and pyconnection.jar into his classpath and uses the GreeterType as follows:
import greeter.*;
...
GreeterFactory factory = new GreeterFactory();
GreeterType g = factory.create("Ugo");
String result = g.greet("Hello")
Ugo can create any number of objects with factory.create, without any knowledge about Python.
How Dan builds greeter.jar
Dan needs:
- pyconnection.jar: download it here.
- generate_wrapper.py: download it here and drop it anywhere in the system search path.
Dan starts with a Python class or package. The Python code is completely independent from Java. It does not need to know anything about Java interfaces or Java types. Of course, it can use the full power of Java if needed, by importing any Java package that is in the classpath.
Dan has his Pyhton code in a directory structure as follows:
MyPythonProject/ |_ Greeter.py |_ ... other python files and/or packages
The file Greeter.py contains the Python class Greeter:
class Greeter(object):
def __init__(self, name):
self.name = name
def greet(self, salutation):
return salutation + ", " + self.name
and it is used from Python as:
>>> from Greeter import Greeter
>>> g = Greeter('Ugo')
>>> g.greet('Hello')
'Hello, Ugo'
All the Python code the Greeter class uses must be under the directory MyPythonProject. The file pyconnection.jar already includes the full Python library. Any other third party module can be installed under MyPythonProject and added to Python's search path (the Python list sys.path).
Once the Python class is working on its own, Dan positions himself inside the directory MyPythonProject and invokes:
$ cd MyPythonProject
$ generate_wrapper.py Greeter GreeterProject
\_____/ \____________/
Python ________| |________ Desired Java
class name project name
After the execution of generate_wrapper.py the directory structure is:
|_ MyPythonProject/ <-------------------+
|_ GreeterProject/ |
|_ src/ | Symbolic
|_ greeter/ | link
| |_ GreeterType.java |
| |_ GreeterFactory.java |
|_ python/ -------------------+
|_ Greeter.py
|_ ... other python files and/or packages
Dan can invoke generate_wrapper.py several times with other class names and the same Java project name, and the proper files will be added to the directory src inside GreeterProject.
Dan edits GreeterType.java to contain the declaration of the public interface for the Greeter Python class:
package greeter;
public interface GreeterType {
public void __init__(String name);
public String greet(String salutation);
}
And voila! Now, Dan just exports GreeterProject as a jar file, say greeter.jar, and he distributes it (see instructions to export using Eclipse). Optionally, Dan also distributes pyconnection.jar, although Ugo User is adviced to download pyconnection.jar once and to drop it in the classpath, ready to use with any number of packages that Dan distributes.
Exporting a jar file with Eclipse
These are the steps Dan follows to export a jar file from GreeterProject.
Launch Eclipse.
Create a project: click in File > New > Java project, select Create project from existing source, and enter the path that ends with the directory GreeterProject. Click Finish.
Export a jar file: right click on GreeterProject in the Package Explorer, select Export, select Java > Jar file, click Next, select the export destination, click Finish.
It is important to make sure that pyconnection.jar is in the classpath for the project GreeterProject before generating the jar file. If Eclipse signals compilation errors for GreeterProject, right click on it, select Build path > Configure Build path , click Add External Jars, and add pyconnection.jar.
The gory details
Here we explain some of the limitations and internals of PyConnection, in case something goes wrong and you need to debug through the wrapper around your Python class.
Warning: not all types are transparently converted between Java and Python yet. Currently, only these types are converted (more to come soon):
Java Python ---- ------ String String Integer Integer HashTable Dictionary Vector List
For the gory++ details, here is the source code of PyConnection, ready to be imported into Eclipse.
Section to be completed
Building pyconnection.jar
To build pyconnection.jar from scratch, just append the contents of the bin directory of PyConnection (containing the Java class files and some Python helper files) to the jar file of a standalone Jython installation.
Updated on: 04:27 PM, Thursday Nov 26, 2009