Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python types for polyglot objects #406

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open

Conversation

antonykamp
Copy link

This PR was developed during the seminar "Softwaredesign" at the Hasso-Plattner Institute Potsdam
Authors: @Olliwehr, @antonykamp
Supervisors: @timfel @JensLincke

What did we change?

This PR introduces the CRUD operations for a register of Python types as equivalents for Java classes.
In the following example, we want to use instances of java.util.ArrayList in the Python code. To have a Python idiomatic interface, we register with polyglot.register_java_interop_type or @polyglot.java_interop_type, a Python type jlist equivalent to java.util.ArrayList.

import java
import polyglot

class jList(__graalpython__.ForeignType):
    def append(self, element):
        self.add(element)

polyglot.register_java_interop_type('java.util.ArrayList', jList)
l = java.type('java.util.ArrayList')()
assert isinstance(l, jList) # == true

We used the existing interoperability extension API as reference.

New operations

We introduced CRUD operations to insert, update, delete, and get mappings and integrated the first three operations into the polyglot module.

Register type mappings with polyglot.register_java_interop_type(javaClassNameAsString, pythonType) or @polyglot.java_interop_type(javaClassNameAsString).

Edit mappings by calling the same function and passing the flag overwrite=True. Otherwise, when registering the same java class two times, it'll throw an Error INTEROP_TYPE_ALREADY_REGISTERED.

Remove a mapping by calling remove_java_interop_type(javaClassNameAsString). If the class is not registered, it will throw an Error INTEROP_TYPE_NOT_REGISTERED.

Whenever we call getClassNode, we look for a registered type for the class. Alternatively, we return Foreign.

How did we change it?

We introduced a WeakHashMap in the Python context. In the map, the keys are string representations of the Java classes, and the values are Python types. In getClassNode, we look the Java classes up in the map. If the key is not found, we return ForeignType as the default Python type.

Additionally, we introduced:

  • register_java_interop_type function,
  • java_interop_type decorator and
  • remove_java_interop_type function

to the polyglot module.

Future work

We identified two possible future work items:

  • support multi-inheritance in Python types,
  • support Java interfaces

Support multi-inheritance in Python types

In Python types, e.g., list, some functions are based on "base functions." The "base function" is building the base for other functions. It would be helpful to inherit from these types, implement the "base function" and get some tasks for free:

class jList(__graalpython__.ForeignType, list):
    def append(self, element):
        self.add(element)

Currently, that's not possible. We receive a "Layout Conflict Error" because ForeignType has some predefined attributes and functions.

TypeError: multiple bases have instance lay-out conflict

We recommend splitting ForeignType into:

  • ForeignType with the current behavior and
  • ForeignTypeBase without the predefined attributes and functions.

If we'd inherit from ForeignTypeBase and list now, we probably wouldn't receive the "Layout Conflict Error" but some functions for free.

Support java interfaces

If we want the Python idiomatic interface for different list classes (ArrayList, LinkedList, etc.), we'd register (and unregister) the same Python type repeatedly.

The idea is that we only register a jlist type for the interface java.util.List, and it's applied to ArrayList, LinkedList, and... by default.

Olliwehr and others added 9 commits June 12, 2024 18:52
…asses

Currently, this enables the registration for custom python classes that should
be associated with certain Java classes.

The registry is part of a PythonContext and is currently indexed by String keys,
which are the java class names for which python classes have been registered.
Registration takes place in a respective PolyglotModuleBuiltin and the lookup
happens in the GetClassNode#getForeign implementation.

Usage in python: `register_java_interop_type("{java_class_name}", pythonClass)`

In the future, also an annotation-style registration way should be implemented.
This enables writing lightweight python classes, which can be used for translating
between the usage of java classes "in the python way" (e.g. [] access).

Example:
```
class jArrayList(__graalpython__.ForeignType):
    # Python world wants to use .append(...) as usual
    def append(self, element):
        # Java ArrayList only knows .add(...), we need to derive from Foreign
        # in order to be able to reroute the message to the original java object
        self.add(element)

register_java_interop_type("java.util.ArrayList", jList)
```

The last line corresponds to the prior commit which introduced the
`register_java_interop_type` function.
Copy link

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
The following contributors of this PR have not signed the OCA:

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application.

When signing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated.

If you are an Oracle employee, please make sure that you are a member of the main Oracle GitHub organization, and your membership in this organization is public.

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. label Jul 5, 2024
@antonykamp antonykamp marked this pull request as ready for review July 5, 2024 11:56
@timfel timfel self-requested a review July 10, 2024 07:26
Copy link

Thank you for signing the OCA.

@oracle-contributor-agreement oracle-contributor-agreement bot added OCA Verified All contributors have signed the Oracle Contributor Agreement. and removed OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. labels Jul 10, 2024
@timfel
Copy link
Member

timfel commented Jul 10, 2024

@Sowasvonbot will integrate this in the next months.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OCA Verified All contributors have signed the Oracle Contributor Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants