Skip to content

Commit cb6b93e

Browse files
committed
[GR-49394] Extend host interop behavior.
PullRequest: graalpython/3030
2 parents 868076d + 4fce5ed commit cb6b93e

28 files changed

+3530
-336
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
1515
* Raise `KeyboardInterrupt` exception when the process is interrupted. Enabled only when using the launcher.
1616
* Add option `python.InitialLocale` to change the default locale. If not set, then Java Locale#getDefault is used.
1717
* `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')`.
18+
* `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).
1819

1920
## Version 23.1.0
2021
* Oracle GraalPy distributions (previously known as GraalPy Enterprise) are now available under the [GFTC license](https://www.oracle.com/downloads/licenses/graal-free-license.html). The community builds published on Github have been renamed to `graalpy-community-<version>-<os>-<arch>.tar.gz`.

docs/user/Interoperability.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,96 @@ Types not listed in the below table have no special interpretation in Python rig
259259
| MetaObject | Any Python `type`. |
260260
| Executable | Anything with a `__call__` method. |
261261
| Instantiable | Any Python `type`. |
262+
263+
## The Truffle Interoperability Extension API
264+
265+
It is possible to extend the Truffle Interoperability protocol directly from python via a simple API defined in the `polyglot` module.
266+
The purpose of this API is to allow custom / user defined types to take part in the interop ecosystem.
267+
This is particularly useful for external types which are not compatible by default with the interop protocol.
268+
An example in this sense are the `numpy` numeric types (e.g., `numpy.int32`) which are not supported by default by the interop protocol.
269+
270+
### The API
271+
272+
| function | Description |
273+
|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
274+
| register_interop_behavior | Takes the receiver **type** as first argument. The remainder keyword arguments correspond to the respective Truffle Interop messages. Not All interop messages are supported. |
275+
| get_registered_interop_behavior | Takes the receiver **type** as first argument. Returns the list of extended Truffle Interop messages for the given type. |
276+
277+
#### Supported messages
278+
279+
The majority (with some exceptions) of the Truffle Interop messages are supported by the interop behavior extension API, as seen in the table below.
280+
The naming convention for the `register_interop_behavior` keyword arguments follows the _snake_case_ naming convention, i.e.: the Truffle Interop `fitsInLong` message
281+
becomes `fits_in_long` and so on. Each message can be extended with a **pure python function** (default keyword arguments, free vars and cell vars are not allowed) or a **boolean constant**.
282+
Following is the list of currently supported interop messages:
283+
284+
| Truffle Message | Extension argument name | Expected return type |
285+
|:-------------------------|:----------------------------|-------------------------------------------------------------------------------------------|
286+
| isBoolean | is_boolean | bool |
287+
| isDate | is_date | bool |
288+
| isDuration | is_duration | bool |
289+
| isIterator | is_iterator | bool |
290+
| isNumber | is_number | bool |
291+
| isString | is_string | bool |
292+
| isTime | is_time | bool |
293+
| isTimeZone | is_time_zone | bool |
294+
| isExecutable | is_executable | bool |
295+
| fitsInBigInteger | fits_in_big_integer | bool |
296+
| fitsInByte | fits_in_byte | bool |
297+
| fitsInDouble | fits_in_double | bool |
298+
| fitsInFloat | fits_in_float | bool |
299+
| fitsInInt | fits_in_int | bool |
300+
| fitsInLong | fits_in_long | bool |
301+
| fitsInShort | fits_in_short | bool |
302+
| asBigInteger | as_big_integer | int |
303+
| asBoolean | as_boolean | bool |
304+
| asByte | as_byte | int |
305+
| asDate | as_date | a python object with the following attributes: `year`, `month`, `day` |
306+
| asDouble | as_double | float |
307+
| asDuration | as_duration | a python object with the following attributes: `seconds`, `nano_adjustment` |
308+
| asFloat | as_float | float |
309+
| asInt | as_int | int |
310+
| asLong | as_long | int |
311+
| asShort | as_short | int |
312+
| asString | as_string | str |
313+
| asTime | as_time | a python object with the following attributes: `hour`, `minute`, `second`, `microsecond` |
314+
| asTimeZone | as_time_zone | a python object with the following attributes: `tm_zone`, `tm_gmtoff` |
315+
| execute | execute | object |
316+
| readArrayElement | read_array_element | object |
317+
| getArraySize | get_array_size | int |
318+
| hasArrayElements | has_array_elements | bool |
319+
| isArrayElementReadable | is_array_element_readable | bool |
320+
| isArrayElementModifiable | is_array_element_modifiable | bool |
321+
| isArrayElementInsertable | is_array_element_insertable | bool |
322+
| isArrayElementRemovable | is_array_element_removable | bool |
323+
| removeArrayElement | remove_array_element | NoneType |
324+
| writeArrayElement | write_array_element | NoneType |
325+
| hasIterator | has_iterator | bool |
326+
| hasIteratorNextElement | has_iterator_next_element | bool |
327+
| getIterator | get_iterator | a python iterator |
328+
| getIteratorNextElement | get_iterator_next_element | object |
329+
| hasHashEntries | has_hash_entries | bool |
330+
| getHashEntriesIterator | get_hash_entries_iterator | a python iterator |
331+
| getHashKeysIterator | get_hash_keys_iterator | a python iterator |
332+
| getHashSize | get_hash_size | int |
333+
| getHashValuesIterator | get_hash_values_iterator | a python iterator |
334+
| isHashEntryReadable | is_hash_entry_readable | bool |
335+
| isHashEntryModifiable | is_hash_entry_modifiable | bool |
336+
| isHashEntryInsertable | is_hash_entry_insertable | bool |
337+
| isHashEntryRemovable | is_hash_entry_removable | bool |
338+
| readHashValue | read_hash_value | object |
339+
| writeHashEntry | write_hash_entry | NoneType |
340+
| removeHashEntry | remove_hash_entry | NoneType |
341+
342+
### Usage Example
343+
344+
```python
345+
import polyglot
346+
347+
class MyType(object):
348+
data = 10
349+
350+
polyglot.register_interop_behavior(MyType,
351+
is_string=True,
352+
as_string=lambda t: f"MyType({t.data})",
353+
)
354+
```

0 commit comments

Comments
 (0)