Skip to content

Commit 53c032d

Browse files
committed
insert more of the proposed blogpost into our docs
1 parent beaa247 commit 53c032d

File tree

1 file changed

+178
-72
lines changed

1 file changed

+178
-72
lines changed

docs/user/JYTHON.md

Lines changed: 178 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,75 +6,181 @@ stable Jython release, and these only come in Python 2.x versions. The GraalVM
66
implementation of Python, in contrast, is only targeting Python 3.x.
77

88
Nonetheless, there are certain features of Jython's Java integration that we can
9-
offer similarly. Some features are more expensive to offer, and thus are hidden
10-
behind a command line flag, `--python.EmulateJython`.
11-
12-
## Importing Java Classes and Packages
13-
14-
In the default mode, Java classes can only be imported through the `java`
15-
package. Additionally, only Java classes can be imported, not packages. In
16-
Jython compatibility mode, importing works more directly, at the cost worse
17-
performance for import failures in the general case.
18-
19-
#### Normal mode
20-
21-
Suppose you want to import the class `sun.misc.Signal`, normal usage looks like
22-
this:
23-
24-
import java.sun.misc.Signal as Signal
25-
26-
For the `java` namespace, you do not have to repeat the `java` name (but can):
27-
28-
import java.lang.System as System
29-
30-
#### Jython mode
31-
32-
In Jython mode, Java packages can be imported and traversed, and they can be
33-
imported directly without going through the `java` module.
34-
35-
import sun.misc.Signal as Signal
36-
37-
import org.antlr
38-
39-
type(org.antlr.v4.runtime) # => module
40-
org.antlr.v4.runtime.Token
41-
42-
The downside of this is that everytime an import fails (and there are many
43-
speculative imports in the standard library), we ask the Java classloader to
44-
list currently available packages and traverse them to check if we should create
45-
a Java package. This slows down startup significantly.
46-
47-
## Interacting with Java Objects
48-
49-
Once you get hold of a Java object or class, interaction in both modes works
50-
naturally. Public fields and methods can be read and invoked as expected.
51-
52-
## Subclassing Java Classes and Implementing Interfaces with Python Classes
53-
54-
This is not supported at all right now, there's no emulation available even in
55-
Jython compatibility mode. We have not seen many uses of this in the wild. Let
56-
us know if this is of interest to you!
57-
58-
## Catching Java exceptions
59-
60-
By default this is not allowed, because of the additional cost of checking for
61-
Java exceptions in the except statement execution. However, in Jython
62-
compatibility mode, the common case of catching a Java exception directly works:
63-
64-
import java.lang.NumberFormatException as NumberFormatException
65-
import java.lang.Integer as Integer
66-
67-
try:
68-
Integer.parseInt("99", 8)
69-
except NumberFormatException as e:
70-
pass
71-
72-
Note that even in this mode, Java exceptions are never caught by generic except
73-
handlers, so this *will not* work:
74-
75-
import java.lang.Integer as Integer
76-
77-
try:
78-
Integer.parseInt("99", 8)
79-
except:
80-
pass
9+
offer similarly. Here is an example:
10+
11+
>>> import java.awt as awt
12+
>>> win = awt.Frame()
13+
>>> win.setSize(200, 200)
14+
>>> win.setTitle("Hello from Python!")
15+
>>> win.getSize().toString()
16+
'java.awt.Dimension[width=200,height=200]'
17+
>>> win.show()
18+
19+
This example works exactly the same on both Jython and Python on GraalVM. Some
20+
features of Jython are more expensive at runtime, and thus are hidden behind a
21+
command line flag on Graal: `--python.EmulateJython`.
22+
23+
### Importing
24+
25+
Import statements allow you to import Java classes, but (unlike Jython), only
26+
packages in the `java` namespace can be directly imported. So this works:
27+
28+
import java.lang as lang
29+
30+
But this doesn't:
31+
32+
import javax.swing as swing
33+
from javax.swing import *
34+
35+
Instead, you'll have to import one of the classes you're interested in directly:
36+
37+
import javax.swing.Window as Window
38+
39+
### Basic Object Usage
40+
41+
Constructing and working with Java objects and classes is done with natural
42+
Python syntax. The methods of Java objects can also be retrieved and passed
43+
around as first class objects (bound to their instance) the same as Python
44+
methods:
45+
46+
>>> from java.util import Random
47+
>>> rg = Random(99)
48+
>>> boundNextInt = rg.nextInt
49+
>>> rg.nextInt()
50+
1491444859
51+
>>> boundNextInt = rg.nextInt
52+
1672896916
53+
54+
### Java-to-Python Types: Automatic Conversion
55+
56+
Method overloads are resolved by matching the Python arguments in a best-effort
57+
manner to the available parameter types. This is also when data conversion
58+
happens. The goal here is to make using Java from Python as smooth as
59+
possible. The matching we do here is similar to Jython, but Graal Python uses a
60+
more dynamic approach to matching — Python types emulating `int` or
61+
`float` are also converted to the appropriate Java types. This allows, for
62+
example, to use Pandas frames as `double[][]` or NumPy array elements as `int[]`
63+
when the elements fit into those Java primitive types.
64+
65+
| Java type | Python type |
66+
|:-----------------------|:----------------------------------------------------------------------------------|
67+
| null | None |
68+
| boolean | bool |
69+
| byte, short, int, long | int, any object that has an `__int__` method |
70+
| float | float, any object that has a `__float__` method |
71+
| char | str of length 1 |
72+
| java.lang.String | str |
73+
| byte[] | bytes, bytearray, wrapped Java array, Python list with only the appropriate types |
74+
| Java arrays | Wrapped Java array or Python list with only the appropriate types |
75+
| Java objects | Wrapped Java object of the appropriate type |
76+
| java.lang.Object | Any object |
77+
78+
### Special Jython Modules
79+
80+
We do not offer any of the special Jython modules. For example, the `jarray`
81+
module on Jython allows construction of primitive Java arrays. This can be
82+
achieved as follows on GraalPython:
83+
84+
>>> import java
85+
>>> java.type("int[]")(10)
86+
87+
Code that only needs to pass a Java array can also use Python types. However,
88+
implicitly this may entail a copy of the array data, which can be deceiving when
89+
using Java arrays as output parameters:
90+
91+
>>> i = java.io.ByteArrayInputStream(b"foobar")
92+
>>> buf = [0, 0, 0]
93+
>>> i.read(buf) # buf is automatically converted to a byte[] array
94+
3
95+
>>> buf
96+
[0, 0, 0] # the converted byte[] array got lost
97+
>>> jbuf = java.type("byte[]")(3)
98+
>>> i.read(jbuf)
99+
3
100+
>>> jbuf
101+
[98, 97, 122]
102+
103+
### Exceptions from Java
104+
105+
Catching all kinds of Java exceptions comes with a performance penalty, and is
106+
only enabled with the `--python.EmulateJython` flag.
107+
108+
>>> import java
109+
>>> v = java.util.Vector()
110+
>>> try:
111+
... x = v.elementAt(7)
112+
... except java.lang.ArrayIndexOutOfBoundsException as e:
113+
... print(e.getMessage())
114+
...
115+
7 >= 0
116+
117+
### Java Collections
118+
119+
There is no automatic mapping of the Python syntax for accessing dictionary
120+
elements to the `java.util` mapping and list classes' ` get`, `set`, or `put`
121+
methods. To use these mapping and list clases, you must call the Java methods:
122+
123+
>>> ht = java.util.Hashtable()
124+
>>> ht.put("foo", "bar")
125+
>>> ht.get("foo")
126+
'bar'
127+
128+
We also do not support Python-style iteration of Java `java.util.Enumerable`,
129+
`java.util.Iterator`, or `java.lang.Iterable`. For these, you'll have to use a
130+
`while` loop and use the `hasNext()` and `next()` (or equivalent) methods.
131+
132+
### No Inheriting from Java
133+
134+
Python classes cannot inherit from Java classes. A workaround can be to create a
135+
flexible subclass in Java, compile it, and use delegation instead. Take this
136+
example:
137+
138+
import java.util.logging.Handler;
139+
140+
public class PythonHandler extends Handler {
141+
private final Value pythonDelegate;
142+
143+
public PythonHandler(Value pythonDelegate) {
144+
this.pythonDelegate = pythonDelegate;
145+
}
146+
147+
public void publish(LogRecord record) {
148+
pythonDelegate.invokeMember("publish", record);
149+
}
150+
151+
public void flush() {
152+
pythonDelegate.invokeMember("flush");
153+
}
154+
155+
public void close() {
156+
pythonDelegate.invokeMember("close");
157+
}
158+
}
159+
160+
Then you can use it like this in Python:
161+
162+
from java.util.logging import LogManager, Logger
163+
164+
class MyHandler():
165+
def publish(self, logRecord): print("[python]", logRecord.toString())​
166+
def flush(): pass​
167+
def close(): pass
168+
169+
LogManager.getLogManager().addLogger(Logger('my.python.logger', None, MyHandler()))
170+
171+
## Embedding Python into Java
172+
173+
The other way to use Jython is to embed it into Java applications. Where above,
174+
Graal Python offered some measure of compatibility with existing Jython code, we
175+
do not offer any in this case. Existing code using Jython depends directly on
176+
the Jython package (for example, in the Maven configuration), because the Java
177+
code has references to Jython internal classes such as `PythonInterpreter`.
178+
179+
For Graal Python, no dependency other than on the [GraalVM
180+
SDK](https://mvnrepository.com/artifact/org.graalvm.sdk/graal-sdk) is
181+
required. There are no APIs particular to Python that are exposed, and
182+
everything is done through the GraalVM API. Important to know is that as long as
183+
your application is executed on a GraalVM with the Python language installed,
184+
you can embed Python in your programs. Please refer to [our embedding
185+
documentation](https://www.graalvm.org/docs/reference-manual/embed/#Function_Python)
186+
for more details.

0 commit comments

Comments
 (0)