Skip to content

Commit 5779738

Browse files
committed
[GR-42711] Inherit from single-trait classes directly
* Avoids Foreign*Type classes. * It means ForeignException is now inherited by all foreign exceptions. * Expose polyglot.ForeignObject and all single-trait classes on the polyglot module.
1 parent 94952ec commit 5779738

File tree

10 files changed

+212
-88
lines changed

10 files changed

+212
-88
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
99
* When calling a method on a foreign object in Python code, Python methods are now prioritized over foreign members.
1010
* 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.
1111
* 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 strings from `str`, foreign iterators from `iterator`, foreign exceptions from `BaseException`, foreign numbers from `ForeignNumberType` and foreign none/null from `NoneType`.
12+
* Foreign lists now inherit from Python `list`, foreign dictionaries from `dict`, foreign strings from `str`, foreign iterators from `iterator`, foreign exceptions from `BaseException`, foreign numbers from `polyglot.ForeignNumber` and foreign none/null from `NoneType`.
1313
* 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.
1414
* See [the documentation](https://github.com/oracle/graalpython/blob/master/docs/user/Interoperability.md#interacting-with-foreign-objects-from-python-scripts) for more information.
1515
* Remove support for running with Sulong managed both in embeddings as well as through the `graalpy-managed` launcher.

docs/user/Interoperability.md

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ Foreign objects are given a Python class corresponding to their interop traits:
6565

6666
```python
6767
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'>]
68+
type(ArrayList()).mro() # => [<class 'polyglot.ForeignList'>, <class 'list'>, <class 'polyglot.ForeignObject'>, <class 'object'>]
69+
type(HashMap()).mro() # => [<class 'polyglot.ForeignDict'>, <class 'dict'>, <class 'polyglot.ForeignObject'>, <class 'object'>]
7070
```
7171

7272
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:
@@ -89,18 +89,7 @@ h |= {3: 6} # {1: 2, 3: 6}
8989
h == {1: 2, 3: 6} # True
9090
```
9191

92-
Specifically:
93-
* Foreign lists inherit from Python `list`
94-
* Foreign dictionaries inherit from `dict`
95-
* Foreign strings inherit from `str`
96-
* Foreign iterators inherit from `iterator`
97-
* Foreign exceptions inherit from `BaseException`
98-
* Foreign numbers inherit from `ForeignNumberType` (since `InteropLibrary` has no way to differentiate integers and floats, but see below)
99-
* Foreign none/null inherit from `NoneType`
100-
* Other foreign objects inherit from `foreign`
101-
102-
Note that Java primitives `byte`, `short`, `int`, `long` and `BigInteger` values are considered Python `int` objects,
103-
and Java primitives `float`, `double` values are considered Python `float` objects.
92+
See [this section](#interop-types-to-python) for more interop traits and how they map to Python types.
10493

10594
## Interacting with other dynamic languages from Python scripts
10695

@@ -226,35 +215,34 @@ The `polyglot` module can be used to expose Python objects to JVM languages and
226215
227216
## Mapping Types between Python and Other Languages
228217
229-
The interop protocol defines different "types" which can overlap in all kinds of ways and have restrictions on how they can interact with Python.
218+
The interop protocol defines different "types/traits" which can overlap in all kinds of ways and have restrictions on how they can interact with Python.
230219
231220
### Interop Types to Python
232221
233-
Most importantly and upfront: all foreign objects passed into Python have the Python type `foreign`.
234-
There is no emulation of (for example) objects that are of interop type "boolean" to have the Python type `bool`.
235-
This is because interop types can overlap in ways that the Python built-in types cannot, and we have yet to define which type should take precedence and such situations.
236-
We do expect to change this in the future, however.
237-
For now, the `foreign` type defines all of the Python special methods for type conversion that are used throughout the interpreter (methods such as `__add__`, `__int__`, `__str__`, `__getitem__`, and so on)
238-
and these try to "do the right thing" based on the interop type (or raise an exception).
222+
All foreign objects passed into Python have the Python type `polyglot.ForeignObject` or a subclass.
239223
240224
Types not listed in the table below have no special interpretation in Python.
241225
242-
| Interop Type | Python Interpretation |
243-
|:--------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
244-
| `null` | `null` is like `None`. Important to know: interop `null` values are all identical to `None`. JavaScript defines two "null-like" values; `undefined` and `null`, which are *not* identical, but when passed to Python, they are treated so. |
245-
| `boolean` | `boolean` behaves like Python booleans, including the fact that in Python, all booleans are also integers (1 and 0 for true and false, respectively). |
246-
| `number` | `number` Behaves like Python numbers. Python only has one integer and one floating point type, but ranges are imported in some places such as typed arrays. |
247-
| `string` | Behaves in the same way as a Python string. |
248-
| `buffer` | Buffers are also a concept in Python's native API (albeit slightly different). Interop buffers are treated in the same was as Python buffers in some places (such as `memoryview`) to avoid copies of data. |
249-
| `array` | An `array` can be used with subscript access in the same way as Python lists, with integers and slices as indices. |
250-
| `hash` | A `hash` can be used with subscript access in the same way as Python dictionaries, with any "hashable" object as a key. "Hashable" follows Python semantics: generally every interop type with an identity is deemed "hashable". Note that if an interop object is of type `Array` **and** `Hash`, the behavior of subscript access is undefined. |
251-
| `members` | An object of type `members` can be read using conventional Python `.` notation or `getattr` and related functions. |
252-
| `iterable` | An `iterable` is treated in the same way as any Python object with an `__iter__` method. That is, it can be used in a loop and other places that accept Python iterables. |
253-
| `iterator` | An `iterator` is treated in the same way as any Python object with a `__next__` method. |
254-
| `exception` | An `exception` can be caught in a generic `except` clause. |
255-
| `MetaObject` | Meta objects can be used in subtype and `isinstance` checks. |
256-
| `executable` | An `executable` object can be executed as a function, but never with keyword arguments. |
257-
| `instantiable` | An `instantiable` object can be called just like a Python type, but never with keyword arguments. |
226+
| Interop Type | Inherits from | Python Interpretation |
227+
|:---------------|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
228+
| `null` | ForeignNone, `NoneType` | `null` is like `None`. Important to know: interop `null` values are all identical to `None`. JavaScript defines two "null-like" values; `undefined` and `null`, which are *not* identical, but when passed to Python, they are treated so. |
229+
| `boolean` | ForeignBoolean | `boolean` behaves like Python booleans, including the fact that in Python, all booleans are also integers (1 and 0 for true and false, respectively). |
230+
| `number` | ForeignNumber | `number` behaves like Python numbers. Python only has one integer and one floating point type, but ranges are imported in some places such as typed arrays. |
231+
| `string` | ForeignString, `str` | Behaves in the same way as a Python string. |
232+
| `buffer` | ForeignObject | Buffers are also a concept in Python's native API (albeit slightly different). Interop buffers are treated in the same was as Python buffers in some places (such as `memoryview`) to avoid copies of data. |
233+
| `array` | ForeignList, `list` | An `array` behaves like a Python `list`. |
234+
| `hash` | ForeignDict, `dict` | A `hash` behaves like a Python `dict`, with any "hashable" object as a key. "Hashable" follows Python semantics: generally every interop type with an identity is deemed "hashable". |
235+
| `members` | ForeignObject | An object of type `members` can be read using conventional Python `.` notation or `getattr` and related functions. |
236+
| `iterable` | ForeignIterable | An `iterable` is treated in the same way as any Python object with an `__iter__` method. That is, it can be used in a loop and other places that accept Python iterables. |
237+
| `iterator` | ForeignIterator, `iterator` | An `iterator` is treated in the same way as any Python object with a `__next__` method. |
238+
| `exception` | ForeignException, `BaseException` | An `exception` can be caught in a generic `except` clause. |
239+
| `MetaObject` | ForeignAbstractClass | Meta objects can be used in subtype and `isinstance` checks. |
240+
| `executable` | ForeignExecutable | An `executable` object can be executed as a function, but never with keyword arguments. |
241+
| `instantiable` | ForeignInstantiable | An `instantiable` object can be called just like a Python type, but never with keyword arguments. |
242+
243+
Foreign numbers inherit from `polyglot.ForeignNumber` and not `int` or `float` because `InteropLibrary` has currently no way to differentiate integers and floats.
244+
However, when foreign numbers are represented as Java primitives `byte`, `short`, `int`, `long`, they are considered Python `int` objects,
245+
and when foreign numbers are represented as Java primitives `float`, `double`, they are considered Python `float` objects.
258246

259247
### Python to Interop Types
260248

@@ -360,8 +348,8 @@ class Main {
360348
```
361349
#### Interop Types
362350
The `register_interop_type` API allows the usage of python classes for foreign objects.
363-
The type of such a foreign object will no longer be `foreign`.
364-
Instead, it will be a generated class with the registered python classes and `foreign` and as super classes.
351+
The class of such a foreign object will no longer be `polyglot.ForeignObject` or `polyglot.Foreign*`.
352+
Instead, it will be a generated class with the registered python classes and `polyglot.ForeignObject` as super class.
365353
This allows custom mapping of foreign methods and attributes to Python's magic methods or more idiomatic Python code.
366354
367355
```java
@@ -409,7 +397,7 @@ import java
409397
from polyglot import register_interop_type
410398

411399
print(my_java_object.getX()) # 42
412-
print(type(my_java_object)) # <class 'foreign'>
400+
print(type(my_java_object)) # <class 'polyglot.ForeignObject'>
413401

414402
class MyPythonClass:
415403
def get_tuple(self):
@@ -421,7 +409,7 @@ register_interop_type(foreign_class, MyPythonClass)
421409

422410
print(my_java_object.get_tuple()) # (42, 17)
423411
print(type(my_java_object)) # <class 'polyglot.Java_org.example.MyJavaClass_generated'>
424-
print(type(my_java_object).mro()) # [polyglot.Java_org.example.MyJavaClass_generated, MyPythonClass, foreign, object]
412+
print(type(my_java_object).mro()) # [polyglot.Java_org.example.MyJavaClass_generated, MyPythonClass, polyglot.ForeignObject, object]
425413

426414
class MyPythonClassTwo:
427415
def get_tuple(self):
@@ -435,7 +423,7 @@ register_interop_type(foreign_class, MyPythonClassTwo, allow_method_overwrites=T
435423

436424
# A newly registered class will be before already registered classes in the mro.
437425
# It allows overwriting methods from already registered classes with the flag 'allow_method_overwrites=True'
438-
print(type(my_java_object).mro()) # [generated_class, MyPythonClassTwo, MyPythonClass, foreign, object]
426+
print(type(my_java_object).mro()) # [generated_class, MyPythonClassTwo, MyPythonClass, polyglot.ForeignObject, object]
439427

440428
print(my_java_object.get_tuple()) # (17, 42)
441429
print(my_java_object) # MyJavaInstance(x=42, y=17)

0 commit comments

Comments
 (0)