layout | toc_group | link_title | permalink | redirect_from |
---|---|---|---|---|
docs |
python |
Jython Compatibility |
/reference-manual/python/Modern-Python-on-JVM/ |
/reference-manual/python/Jython/ |
For Python version 2 (now EOL), Jython is the de facto means of interfacing Python and Java. Most existing Jython code that uses Java integration will be based on a stable Jython release—however, these are only available in Python 2.x versions. GraalPy, in contrast, is compatible with Python 3.x and does not provide full compatibility with earlier 2.x versions of Jython.
To migrate code from Python 2 to Python 3, follow the official guide from the Python community. Once your Jython code is compatible with Python 3, follow this guide to iron out other differences between GraalPy and Jython.
GraalPy's first-class support for Java interoperability makes using Java libraries from Python as easy as possible, with special affordances for Java code beyond the generic interoperability support for other Graal languages (languages implemented on the Truffle framework).
Not all features of Jython are supported on GraalPy.
Some are supported, but disabled by default due to their negative impact on runtime performance.
During migration, you can enable these features using a command line option: --python.EmulateJython
.
We recommend to move away from these features, however, to achieve optimal performance.
To move plain Jython scripts from Jython to GraalPy, use a GraalPy JVM-based runtime. (For more information, see available GraalPy Distributions).
There are certain features of Jython's Java integration that are enabled by default on GraalPy. Here is an example:
>>> import java.awt as awt
>>> win = awt.Frame()
>>> win.setSize(200, 200)
>>> win.setTitle("Hello from Python!")
>>> win.getSize().toString()
'java.awt.Dimension[width=200,height=200]'
>>> win.show()
This example produces the same result when run on both Jython and GraalPy.
However, when the example is run on GraalPy, only packages that are in the java
namespace can be imported directly.
To import classes from packages outside the java
namespace, use the --python.EmulateJython
option.
Note: When embedding GraalPy in a modularized application, you may have to add exports for the required modules according to JSR 376.
Additionally, it is not possible to import Java packages as Python modules in all circumstances. For example, this will work:
import java.lang as lang
But, this will not work:
import javax.swing as swing
from javax.swing import *
Instead, import one of the classes directly:
import javax.swing.Window as Window
Constructing and working with Java objects and classes is achieved with conventional Python syntax. The methods of a Java object can also be retrieved and referenced as first class objects (bound to their instance), in the same way as Python methods. For example:
>>> from java.util import Random
>>> rg = Random(99)
>>> rg.nextInt()
1491444859
>>> boundNextInt = rg.nextInt
>>> boundNextInt()
1672896916
Method overloads are resolved by matching the Python arguments in a best-effort manner to the available parameter types.
This approach is also taken when converting data.
The goal here is to make using Java from Python as smooth as possible.
The matching approach taken by GraalPy is similar to Jython, but GraalPy uses a more dynamic approach to matching—Python types emulating int
or float
are also converted to the appropriate Java types.
This enables you, for example, to use a Pandas frame as double[][]
or NumPy array elements as int[]
when the elements fit into those Java primitive types.
Java type | Python type |
---|---|
null |
None |
boolean |
bool |
byte , short , int , long |
int , any object that has an __int__ method |
float |
float , any object that has a __float__ method |
char |
str of length 1 |
java.lang.String |
str |
byte[] |
bytes , bytearray , wrapped Java array , Python list with only the appropriate types |
Java arrays | Wrapped Java array or Python list with only the appropriate types |
Java objects | Wrapped Java object of the appropriate type |
java.lang.Object |
Any object |
GraalPy implements the jarray
module (to create primitive Java arrays) for compatibility.
For example:
>>> import jarray
>>> jarray.array([1,2,3], 'i')
Note that its usage is equivalent to constructing the array type using the java.type
function and then populating the array, as follows:
>>> import java
>>> java.type("int[]")(10)
The code that creates a Java array can also use Python types. However, implicitly, this may produce a copy of the array data, which can be deceptive when using a Java array as an output parameter:
>>> i = java.io.ByteArrayInputStream(b"foobar")
>>> buf = [0, 0, 0]
>>> i.read(buf) # buf is automatically converted to a byte[] array
3
>>> buf
[0, 0, 0] # the converted byte[] array is lost
>>> jbuf = java.type("byte[]")(3)
>>> i.read(jbuf)
3
>>> jbuf
[98, 97, 122]
To catch Java exceptions, use the --python.EmulateJython
option.
Note: Catching a Java exception incurs a performance penalty.
For example:
>>> import java
>>> v = java.util.Vector()
>>> try:
... x = v.elementAt(7)
... except java.lang.ArrayIndexOutOfBoundsException as e:
... print(e.getMessage())
...
7 >= 0
-
Java arrays and collections that implement the
java.util.Collection
interface can be accessed using the[]
syntax. An empty collection is consideredfalse
in boolean conversions. The length of a collection is exposed by thelen
built-in function. For example:>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> l.add("baz") True >>> l[0] 'foo' >>> l[1] = "bar" >>> del l[1] >>> len(l) 1 >>> bool(l) True >>> del l[0] >>> bool(l) False
-
Java iterables that implement the
java.lang.Iterable
interface can be iterated over using afor
loop or theiter
built-in function and are accepted by all built-ins that expect an iterable. For example:>>> [x for x in l] ['foo', 'bar'] >>> i = iter(l) >>> next(i) 'foo' >>> next(i) 'bar' >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> set(l) {'foo', 'bar'}
-
An iterator can be iterated as well. For example:
>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> i = l.iterator() # Calls the Java iterator methods >>> next(i) 'foo'
-
Mapped collections that implement the
java.util.Map
interface can be accessed using the[]
notation. An empty map is consideredfalse
in boolean conversions. Iteration of a map yields its keys, consistent withdict
. For example:>>> from java.util import HashMap >>> m = HashMap() >>> m['foo'] = 5 >>> m['foo'] 5 >>> m['bar'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: bar >>> [k for k in m] ['foo'] >>> bool(m) True >>> del m['foo'] >>> bool(m) False
Inheriting from a Java class (or implementing a Java interface) is supported with some syntactical differences from Jython.
To create a class that inherits from a Java class (or implements a Java interface), use the conventional Python class
statement: declared methods
override (implement) superclass (interface) methods when their names match.
To call the a superclass method, use the special attribute self.__super__
.
The created object does not behave like a Python object but instead in the same way as a foreign Java object.
Its Python-level members can be accessed using its this
attribute. For example:
import atexit
from java.util.logging import Logger, Handler
class MyHandler(Handler):
def __init__(self):
self.logged = []
def publish(self, record):
self.logged.append(record)
logger = Logger.getLogger("mylog")
logger.setUseParentHandlers(False)
handler = MyHandler()
logger.addHandler(handler)
# Make sure the handler is not used after the Python context has been closed
atexit.register(lambda: logger.removeHandler(handler))
logger.info("Hi")
logger.warning("Bye")
# The python attributes/methods of the object are accessed through 'this' attribute
for record in handler.this.logged:
print(f'Python captured message "{record.getMessage()}" at level {record.getLevel().getName()}')
The other way to use Jython was to embed it into a Java application. There were two options for such an embedding.
-
Use the
PythonInterpreter
object that Jython provides. Existing code using Jython in this manner depends directly on the Jython package (for example, in the Maven configuration), because the Java code has references to Jython internal classes. These classes do not exist in GraalVM, and no equivalent classes are exposed. To migrate from this usage, switch to the GraalVM SDK. Using this SDK, no APIs particular to Python are exposed, everything is achieved via the GraalVM API, with maximum configurability of the Python runtime. Refer to the Getting Started documentation for preparing a setup. -
Embed Jython in Java via JSR 223 by using the classes of the the
javax.script
package, and, in particular, via theScriptEngine
class. We do not recommend this approach, because theScriptEngine
APIs are not a clean fit for the options and capabilities of GraalPy. However, to migrate existing code, we provide an example ScriptEngine implementation that you can inline into your project. Refer to the reference manual for embedding for details.