Skip to content

Commit a7d0248

Browse files
committed
[GR-42711] Give a composed class based on interop traits for foreign objects
PullRequest: graalpython/3428
2 parents ed70c57 + bcd7d27 commit a7d0248

File tree

79 files changed

+3025
-1885
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+3025
-1885
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ language runtime. The main focus is on user-observable behavior of the engine.
77
* Updated developer metadata of Maven artifacts.
88
* Added gradle plugin for polyglot embedding of Python packages into Java.
99
* When calling a method on a foreign object in Python code, Python methods are now prioritized over foreign members.
10+
* Added `polyglot.register_interop_type` and `@polyglot.interop_type` to define custom Python methods for a given foreign class/type. See [the documentation](https://github.com/oracle/graalpython/blob/master/docs/user/Interoperability.md#the-interoperability-extension-api) for more information.
11+
* Foreign objects are now given a Python class corresponding to their interop traits.
12+
** Foreign lists now inherit from Python `list`, foreign dictionaries from `dict`, foreign iterators from `iterator`, foreign exceptions from `BaseException` and foreign none/null from `NoneType`.
13+
** This means all Python methods of these types are available on the corresponding foreign objects, which behave as close as possible as if they were Python objects.
14+
** See [the documentation](https://github.com/oracle/graalpython/blob/master/docs/user/Interoperability.md#interacting-with-foreign-objects-from-python-scripts) for more information.
1015

1116
## Version 24.1.0
1217
* GraalPy is now considered stable for pure Python workloads. While many workloads involving native extension modules work, we continue to consider them experimental. You can use the command-line option `--python.WarnExperimentalFeatures` to enable warnings for such modules at runtime. In Java embeddings the warnings are enabled by default and you can suppress them by setting the context option 'python.WarnExperimentalFeatures' to 'false'.
1318
* Update to Python 3.11.7.
1419
* We now provide intrinsified `_pickle` module also in the community version.
15-
* `polyglot.eval` now raises more meaningful exceptions. Unavaliable languages raise `ValueError`. Exceptions from the polyglot language are raised directly as interop objects (typed as `polyglot.ForeignException`). The shortcut for executing python files without specifying language has been removed, use regular `eval` for executing Python code.
20+
* `polyglot.eval` now raises more meaningful exceptions. Unavailable languages raise `ValueError`. Exceptions from the polyglot language are raised directly as interop objects (typed as `polyglot.ForeignException`). The shortcut for executing python files without specifying language has been removed, use regular `eval` for executing Python code.
1621
* In Jython emulation mode we now magically fall back to calling Java getters or setters when using Python attribute access for non-visible properties. This can help migrating away from Jython if you relied on this behavior.
1722
* The option `python.EmulateJython` to enable Jython emulation is now marked as stable, and can thus be relied upon in production.
1823
* Fixed parsing of pyvenv.cfg according to PEP 405, which is required to use [uv](https://github.com/astral-sh/uv?tab=readme-ov-file#uv) generated venvs with GraalPy.
@@ -33,7 +38,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
3338
* Add option `python.InitialLocale` to change the default locale. If not set, then Java Locale#getDefault is used.
3439
* `multiprocessing` module now uses the `spawn` method (creates new processes) by default. The formerly default method that uses threads and multiple Truffle contexts can be selected using `multiprocessing.set_start_method('graalpy')`.
3540
* `polyglot` module: add API to redefine Truffle interop messages for external / user defined types. For more details see [The Truffle Interoperability Extension API](docs/user/Interoperability.md).
36-
*Adding integration with jBang (https://www.jbang.dev/)
41+
* Adding integration with jBang (https://www.jbang.dev/)
3742
** running example via `jbang hello@oracle/graalpython` or `jbang hello@oracle/graalpython "print(1*4)"`
3843
** creating new script via: `jbang init --template=graalpy@oracle/graalpython myscript.java`
3944
** creating new script with local maven repo for testing: `jbang init --template=graalpy_local_repo@oracle/graalpython -Dpath_to_local_repo=/absolute/path/to/local/maven/repository myscript.java'

docs/user/Interoperability.md

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ byteArray = myBigInt.toByteArray()
2626
assert len(byteArray) == 1 and byteArray[0] == 42
2727
```
2828

29-
<aside markdown="1">
30-
For plain Python users, the `java` module is only available when running on the JVM distribution.
31-
</aside>
32-
3329
To import packages from the `java` namespace, you can also use the conventional Python import syntax:
3430
```python
3531
import java.util.ArrayList
@@ -46,7 +42,7 @@ In addition to the `type` built-in method, the `java` module exposes the followi
4642

4743
Built-in | Specification
4844
--- | ---
49-
`instanceof(obj, class)` | returns `True` if `obj` is an instance of `class` (`class` must be a foreign object class)
45+
`instanceof(obj, class)` | returns `True` if `obj` is an instance of `class` (`class` must be a foreign object class)
5046
`is_function(obj)` | returns `True` if `obj` is a Java host language function wrapped using interop
5147
`is_object(obj)` | returns `True` if `obj` if the argument is Java host language object wrapped using interop
5248
`is_symbol(obj)` | returns `True` if `obj` if the argument is a Java host symbol, representing the constructor and static members of a Java class, as obtained by `java.type`
@@ -63,6 +59,43 @@ assert java.instanceof(my_list, ArrayList)
6359

6460
See [Polyglot Programming](https://github.com/oracle/graal/blob/master/docs/reference-manual/polyglot-programming.md) and [Embed Languages](https://github.com/oracle/graal/blob/master/docs/reference-manual/embedding/embed-languages.md) for more information about interoperability with other programming languages.
6561

62+
## Interacting with foreign objects from Python scripts
63+
64+
Foreign objects are given a Python class corresponding to their interop traits:
65+
66+
```python
67+
from java.util import ArrayList, HashMap
68+
type(ArrayList()).mro() # => [<class 'polyglot.ForeignList'>, <class 'list'>, <class 'foreign'>, <class 'object'>]
69+
type(HashMap()).mro() # => [<class 'polyglot.ForeignDict'>, <class 'dict'>, <class 'foreign'>, <class 'object'>]
70+
```
71+
72+
This means all Python methods of these types are available on the corresponding foreign objects, which behave as close as possible as if they were Python objects:
73+
74+
```python
75+
from java.util import ArrayList, HashMap
76+
l = ArrayList()
77+
l.append(1) # l: [1]
78+
l.extend([2, 3]) # l: [1, 2, 3]
79+
l.add(4) # l: [1, 2, 3, 4] # we can still call Java methods, this is calling ArrayList#add
80+
l[1:3] # => [2, 3]
81+
l.pop(1) # => 2; l: [1, 3, 4]
82+
l.insert(1, 2) # l: [1, 2, 3, 4]
83+
l == [1, 2, 3, 4] # True
84+
85+
h = HashMap()
86+
h[1] = 2 # h: {1: 2}
87+
h.setdefault(3, 4) # h: {1: 2, 3: 4}
88+
h |= {3: 6} # {1: 2, 3: 6}
89+
h == {1: 2, 3: 6} # True
90+
```
91+
92+
Specifically:
93+
* Foreign lists inherit from Python `list`
94+
* Foreign dictionaries inherit from `dict`
95+
* Foreign iterators inherit from `iterator`
96+
* Foreign exceptions inherit from `BaseException`
97+
* Foreign none/null inherit from `NoneType`
98+
6699
## Interacting with other dynamic languages from Python scripts
67100

68101
More general, non-JVM specific interactions with other languages from Python scripts are achieved via the _polyglot_ API.

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ public void javaStringsAndPythonStrings() throws IOException {
584584
Map<String, String> source = Map.of("source", "foo");
585585
context.getBindings("python").getMember("enrich").execute(source);
586586
assertEquals("" +
587-
"<class 'foreign'>\n" +
587+
"<class 'polyglot.ForeignDict'>\n" +
588588
"True\n" +
589589
"True\n", out.toString("UTF-8"));
590590
}

0 commit comments

Comments
 (0)