- GraalVM for JDK 23(最新)
- GraalVM for JDK 24(アーリーアクセス)
- GraalVM for JDK 21
- GraalVM for JDK 17
- アーカイブ
- 開発ビルド
相互運用性
GraalPy は主に Java アプリケーションでの使用が推奨されていますが、他の Graal 言語(Truffle フレームワーク上に実装された言語)と相互運用できます。これは、他の言語が提供するオブジェクトや関数を Python スクリプトから直接使用できることを意味します。
Python スクリプトから Java との対話 #
Java は JVM のホスト言語であり、GraalPy インタープリター自体を実行します。Python スクリプトから Java と相互運用するには、java モジュールを使用します。
import java
BigInteger = java.type("java.math.BigInteger")
myBigInt = BigInteger.valueOf(42)
# a public Java methods can just be called
myBigInt.shiftLeft(128) # returns a <JavaObject[java.math.BigInteger] at ...>
# Java method names that are keywords in Python must be accessed using `getattr`
getattr(myBigInt, "not")() # returns a <JavaObject[java.math.BigInteger] at ...>
byteArray = myBigInt.toByteArray()
# Java arrays can act like Python lists
assert len(byteArray) == 1 and byteArray[0] == 42
java 名前空間からパッケージをインポートするには、従来の Python の import 構文を使用することもできます。
import java.util.ArrayList
from java.util import ArrayList
assert java.util.ArrayList == ArrayList
al = ArrayList()
al.add(1)
al.add(12)
assert list(al) == [1, 12]
type 組み込みメソッドに加えて、java モジュールは次のメソッドを公開します。
| 組み込み | 仕様 |
|---|---|
instanceof(obj, class) |
obj が class のインスタンスである場合(class は外部オブジェクトクラスである必要があります)、True を返します。 |
is_function(obj) |
obj が interop を使用してラップされた Java ホスト言語関数である場合、True を返します。 |
is_object(obj) |
obj が interop を使用してラップされた Java ホスト言語オブジェクトの場合、True を返します。 |
is_symbol(obj) |
obj が java.type で取得した Java クラスのコンストラクターおよび静的メンバーを表す Java ホストシンボルの場合、True を返します。 |
ArrayList = java.type('java.util.ArrayList')
my_list = ArrayList()
assert java.is_symbol(ArrayList)
assert not java.is_symbol(my_list)
assert java.is_object(ArrayList)
assert java.is_function(my_list.add)
assert java.instanceof(my_list, ArrayList)
他のプログラミング言語との相互運用性について詳しくは、ポリグロットプログラミングおよび言語の埋め込みをご覧ください。
Python スクリプトから他の動的言語との対話 #
より一般的で、JVM に固有ではない、Python スクリプトからの他の言語との対話は、ポリグロット API を介して実現されます。これには、JavaScript や Ruby など、Truffle フレームワークを介してサポートされる動的言語とのすべての対話が含まれます。
他の動的言語のインストール #
他の言語は、GraalPy と同様に、それぞれの Maven 依存関係を使用することで含めることができます。たとえば、すでに GraalPy で Maven プロジェクトを構成している場合は、次の依存関係を追加して JavaScript にアクセスできます。
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>24.1.0</version>
</dependency>
例 #
- 他の言語と対話するには、
polyglotモジュールをインポートします。import polyglot - 別の言語でインラインコードを評価します。
assert polyglot.eval(string="1 + 1", language="js") == 2 - ファイルからコードを評価します。
with open("./my_js_file.js", "w") as f: f.write("Polyglot.export('JSMath', Math)") polyglot.eval(path="./my_js_file.js", language="js") - ポリグロットスコープからグローバル値をインポートします。
Math = polyglot.import_value("JSMath")このグローバル値は、期待どおりに動作するはずです。
- 属性へのアクセスは、ポリグロットメンバー名前空間から読み取ります。
assert Math.E == 2.718281828459045 - 結果に対してメソッドを呼び出すと、ストレート
invokeが試行され、メンバーの読み取りと実行の試行にフォールバックします。assert Math.toString() == "[object Math]" - アイテムへのアクセスは、文字列と数値の両方でサポートされています。
assert Math["PI"] == 3.141592653589793
- 属性へのアクセスは、ポリグロットメンバー名前空間から読み取ります。
- JavaScript の正規表現エンジンを使用して、Python 文字列を一致させます。
js_re = polyglot.eval(string="RegExp()", language="js") pattern = js_re.compile(".*(?:we have (?:a )?matching strings?(?:[!\\?] )?)(.*)") if pattern.exec("This string does not match"): raise SystemError("that shouldn't happen") md = pattern.exec("Look, we have matching strings! This string was matched by Graal.js") assert "Graal.js" in md[1]このプログラムは、JavaScript の正規表現オブジェクトを使用して Python 文字列を一致させます。Python は JavaScript の結果からキャプチャされたグループを読み取り、その中にサブストリングがあるかどうかを確認します。
他の言語への Python オブジェクトのエクスポート #
polyglot モジュールを使用して、Python オブジェクトを JVM 言語およびその他の Graal 言語(Truffle フレームワーク上に実装された言語)に公開できます。
- Python から他の言語にオブジェクトをエクスポートして、インポートできるようにすることができます。
import ssl polyglot.export_value(value=ssl, name="python_ssl")次に、(たとえば)JavaScript コードからそれを使用します。
Polyglot.import('python_ssl).get_server_certificate(["oracle.com", 443]) - Python 関数をデコレートして、名前でエクスポートできます。
@polyglot.export_value def python_method(): return "Hello from Python!"次に、(たとえば)Java コードからそれを使用します。
import org.graalvm.polyglot.*; class Main { public static void main(String[] args) { try (var context = Context.create()) { context.eval(Source.newBuilder("python", "file:///python_script.py").build()); String result = context. getPolyglotBindings(). getMember("python_method"). execute(). asString(); assert result.equals("Hello from Python!"); } } }
Python と他の言語間の型マッピング #
interop プロトコルは、さまざまな「型」を定義しており、それらはあらゆる方法で重複する可能性があり、Python との相互作用方法に制限があります。
Python への Interop 型 #
最も重要で、最初に言っておくべきことは、Python に渡されるすべての外部オブジェクトは、Python 型 foreign を持つということです。たとえば、interop 型が「boolean」のオブジェクトを Python 型 bool にエミュレートすることはありません。これは、interop 型が Python の組み込み型では不可能な方法で重複する可能性があり、どの型を優先すべきか、またそのような状況をまだ定義していないためです。ただし、将来的にはこれを変更する予定です。今のところ、foreign 型は、インタープリター全体で使用される型変換のためのすべての Python 特殊メソッド(__add__、__int__、__str__、__getitem__ など)を定義しており、これらは interop 型に基づいて「正しいことをしようとする」(または例外を発生させる)。
以下の表にリストされていない型は、Python での特別な解釈はありません。
| Interop 型 | Python の解釈 |
|---|---|
null |
null は None のようなものです。重要な注意点:interop の null 値はすべて None と同一です。JavaScript は 2 つの「null のような」値、undefined と null を定義していますが、これらは同一ではありませんが、Python に渡されると、同一として扱われます。 |
boolean |
boolean は、Python のブール値と同様に動作します。Python では、すべてのブール値も整数(true は 1、false は 0)であるという事実も含まれます。 |
number |
number は、Python の数値のように動作します。Python には整数型と浮動小数点型が 1 つしかありませんが、型付き配列など、一部の場所では範囲がインポートされます。 |
string |
Python 文字列と同じように動作します。 |
buffer |
バッファーは、Python のネイティブ API の概念でもあります(ただし、わずかに異なります)。interop バッファーは、データのコピーを避けるために、一部の場所(memoryview など)で Python バッファーと同じように扱われます。 |
array |
array は、Python リストと同様に、整数とスライスをインデックスとして使用して、添え字アクセスで使用できます。 |
hash |
hash は、Python ディクショナリと同様に、キーとして任意の「ハッシュ可能」オブジェクトを使用して、添え字アクセスで使用できます。「ハッシュ可能」は Python セマンティクスに従います。一般に、ID を持つすべての interop 型は「ハッシュ可能」と見なされます。interop オブジェクトが Array および Hash 型である場合、添え字アクセスの動作は未定義であることに注意してください。 |
members |
members 型のオブジェクトは、従来の Python の . 表記または getattr および関連関数を使用して読み取ることができます。 |
iterable |
iterable は、__iter__ メソッドを持つ任意の Python オブジェクトと同様に扱われます。つまり、ループや Python イテラブルを受け入れる他の場所で使用できます。 |
iterator |
iterator は、__next__ メソッドを持つ任意の Python オブジェクトと同様に扱われます。 |
exception |
exception は、一般的な except 句でキャッチできます。 |
MetaObject |
メタオブジェクトは、サブタイプと isinstance チェックで使用できます。 |
executable |
executable オブジェクトは関数として実行できますが、キーワード引数を使用することはできません。 |
instantiable |
instantiable オブジェクトは、Python 型のように呼び出すことができますが、キーワード引数を使用することはできません。 |
Interop 型への Python #
| Interop 型 | Python の解釈 |
|---|---|
null |
None のみ。 |
boolean |
Python bool のサブタイプのみ。Python セマンティクスとは対照的に、Python bool は決して interop 数値でもないことに注意してください。 |
number |
int および float のサブタイプのみ。 |
string |
str のサブタイプのみ。 |
array |
__getitem__ および __len__ メソッドを持つオブジェクト。ただし、keys、values、および items メソッドも持つ場合は除きます(dict と同様)。 |
hash |
dict のサブタイプのみ。 |
members |
任意の Python オブジェクト。読み取り/書き込みのルールは少しアドホックであることに注意してください。Python MOP の一部ではないため、チェックすることはできません。 |
iterable |
__iter__ または __getitem__ メソッドを持つ任意の Python オブジェクト。 |
iterator |
__next__ メソッドを持つ任意の Python オブジェクト。 |
exception |
任意の Python BaseException サブタイプ。 |
MetaObject |
任意の Python type。 |
executable |
__call__ メソッドを持つ任意の Python オブジェクト。 |
instantiable |
任意の Python type。 |
相互運用性拡張 API #
polyglot モジュールで定義された単純な API を介して、Python から相互運用性プロトコルを直接拡張することが可能です。この API の目的は、カスタム/ユーザー定義型が interop エコシステムに参加できるようにすることです。これは、interop プロトコルとデフォルトでは互換性がない外部型に特に役立ちます。この意味での例は、interop プロトコルでデフォルトでサポートされていない numpy 数値型(たとえば、numpy.int32)です。
API #
| 関数 | 説明 |
|---|---|
| register_interop_behavior | 最初の引数としてレシーバー型を取ります。残りのキーワード引数は、それぞれの interop メッセージに対応します。すべての interop メッセージがサポートされているわけではありません。 |
| get_registered_interop_behavior | 最初の引数としてレシーバー型を取ります。指定された型の拡張された interop メッセージのリストを返します。 |
| @interop_behavior | クラスデコレーターは、唯一の引数としてレシーバー型を取ります。interop メッセージは、デコレートされたクラス(サプライヤー)で定義された静的メソッドを介して拡張されます。 |
サポートされているメッセージ
次の表に示すように、interop メッセージの大部分(いくつかの例外を除く)は、interop 動作拡張 API でサポートされています。register_interop_behavior キーワード引数の命名規則は、snake_case の命名規則に従います。つまり、interop fitsInLong メッセージは fits_in_long になります。各メッセージは、純粋な Python 関数(デフォルトのキーワード引数、フリー変数、およびセル変数は許可されていません)またはブール定数で拡張できます。次の表に、サポートされている interop メッセージについて説明します。
| メッセージ | 拡張引数名 | 予想される戻り値の型 |
|---|---|---|
| isBoolean | is_boolean | bool |
| isDate | is_date | bool |
| isDuration | is_duration | bool |
| isIterator | is_iterator | bool |
| isNumber | is_number | bool |
| isString | is_string | bool |
| isTime | is_time | bool |
| isTimeZone | is_time_zone | bool |
| isExecutable | is_executable | bool |
| fitsInBigInteger | fits_in_big_integer | bool |
| fitsInByte | fits_in_byte | bool |
| fitsInDouble | fits_in_double | bool |
| fitsInFloat | fits_in_float | bool |
| fitsInInt | fits_in_int | bool |
| fitsInLong | fits_in_long | bool |
| fitsInShort | fits_in_short | bool |
| asBigInteger | as_big_integer | int |
| asBoolean | as_boolean | bool |
| asByte | as_byte | int |
| asDate | as_date | 次の要素を持つ3要素タプル: (year: int, month: int, day: int) |
| asDouble | as_double | float |
| asDuration | as_duration | 次の要素を持つ2要素タプル: (seconds: long, nano_adjustment: long) |
| asFloat | as_float | float |
| asInt | as_int | int |
| asLong | as_long | int |
| asShort | as_short | int |
| asString | as_string | str |
| asTime | as_time | 次の要素を持つ4要素タプル: (hour: int, minute: int, second: int, microsecond: int) |
| asTimeZone | as_time_zone | 文字列 (タイムゾーン) または整数 (UTCからの秒単位の差) |
| execute | execute | object |
| readArrayElement | read_array_element | object |
| getArraySize | get_array_size | int |
| hasArrayElements | has_array_elements | bool |
| isArrayElementReadable | is_array_element_readable | bool |
| isArrayElementModifiable | is_array_element_modifiable | bool |
| isArrayElementInsertable | is_array_element_insertable | bool |
| isArrayElementRemovable | is_array_element_removable | bool |
| removeArrayElement | remove_array_element | NoneType |
| writeArrayElement | write_array_element | NoneType |
| hasIterator | has_iterator | bool |
| hasIteratorNextElement | has_iterator_next_element | bool |
| getIterator | get_iterator | Pythonイテレータ |
| getIteratorNextElement | get_iterator_next_element | object |
| hasHashEntries | has_hash_entries | bool |
| getHashEntriesIterator | get_hash_entries_iterator | Pythonイテレータ |
| getHashKeysIterator | get_hash_keys_iterator | Pythonイテレータ |
| getHashSize | get_hash_size | int |
| getHashValuesIterator | get_hash_values_iterator | Pythonイテレータ |
| isHashEntryReadable | is_hash_entry_readable | bool |
| isHashEntryModifiable | is_hash_entry_modifiable | bool |
| isHashEntryInsertable | is_hash_entry_insertable | bool |
| isHashEntryRemovable | is_hash_entry_removable | bool |
| readHashValue | read_hash_value | object |
| writeHashEntry | write_hash_entry | NoneType |
| removeHashEntry | remove_hash_entry | NoneType |
使用例 #
既存の型に対して相互運用動作を登録するためのシンプルなregister_interop_behavior APIが利用可能です。
import polyglot
import numpy
polyglot.register_interop_behavior(numpy.int32,
is_number=True,
fitsInByte=lambda v: -128 <= v < 128,
fitsInShort=lambda v: -0x8000 <= v < 0x8000
fitsInInt=True,
fitsInLong=True,
fitsInBigInteger=True,
asByte=int,
asShort=int,
asInt=int,
asLong=int,
asBigInteger=int,
)
より多くの動作を宣言する際には、@interop_behaviorデコレータがより便利かもしれません。相互運用メッセージの拡張は、デコレートされたクラスの静的メソッドを介して実現されます。静的メソッドの名前は、register_interop_behaviorで期待されるキーワード名と同一です。
from polyglot import interop_behavior
import numpy
@interop_behavior(numpy.float64)
class Int8InteropBehaviorSupplier:
@staticmethod
def is_number(_):
return True
@staticmethod
def fitsInDouble(_):
return True
@staticmethod
def asDouble(v):
return float(v)
これにより、両方のクラスは埋め込まれたときに期待どおりに動作します。
import java.nio.file.Files;
import java.nio.file.Path;
import org.graalvm.polyglot.Context;
class Main {
public static void main(String[] args) {
try (var context = Context.create()) {
context.eval("python", Files.readString(Path.of("path/to/interop/behavior/script.py")));
assert context.eval("python", "numpy.float64(12)").asDouble() == 12.0;
assert context.eval("python", "numpy.int32(12)").asByte() == 12;
}
}
}