相互運用性

ポリグロットAPI

GraalVMでは、JavaScript、R、Rubyや、LLVMビットコードにコンパイルされるものを含め、他の様々なプログラミング言語がサポートされているため、これらとやり取りするためのPython APIも提供されます。実際、GraalVMでは、このAPIを内部的に使用し、GraalVM LLVMランタイムを使用してPython C拡張機能が実行されます。

polyglotモジュールをインポートして、他の言語を操作できます:

import polyglot

ポリグロット・スコープ全体からグローバル値をインポートできます:

imported_polyglot_global = polyglot.import_value("global_name")

その後、このグローバル値は予期したとおりに動作します:

別の言語のインライン化されたコードを評価できます:

polyglot.eval(string="1 + 1", language="ruby")

パスを渡すことによって、ファイル内のコードを評価できます:

polyglot.eval(path="./my_ruby_file.rb", language="ruby")

ファイルを渡す場合、ファイルベースの言語検出を利用することもできます:

polyglot.eval(path="./my_ruby_file.rb")

Pythonからサポートされている他の言語にオブジェクトをエクスポートして、それをインポートできるようにすることができます:

foo = object()
polyglot.export_value(foo, name="python_foo")

エクスポート関数はデコレータとして使用できます。この場合、関数名がグローバルにエクスポートされた名前として使用されます:

@polyglot.export_value
def python_method():
    return "Hello from Python!"

JavaScript正規表現エンジンを使用してPython文字列を照合する方法の例を次に示します。次のコードをpolyglot_example.pyファイルに保存します:

import polyglot

re = polyglot.eval(string="RegExp()", language="js")

pattern = 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")
if not md:
    raise SystemError("this should have matched")

print("Here is what we found: '%s'" % md[1])

これを実行するには、--jvm --polyglotオプションをgraalpythonランチャに渡します:

graalpython --jvm --polyglot polyglot_example.py

このプログラムは、JavaScript正規表現オブジェクトを使用してPython文字列を照合します。Pythonは、取得したグループをJavaScriptの結果から読み取り、Here is what we found: 'This string was matched by Graal.js'と出力します。

より複雑な例として、Rを使用してファイルを読み取り、Pythonでそのデータを処理して、Rを再度使用し、RライブラリとPythonライブラリの両方を組み合せて使用して結果のデータ・イメージを表示する方法を確認します。この例を実行するには、まず、必要なRライブラリをインストールします:

R -e 'install.packages("https://www.rforge.net/src/contrib/jpeg_0.1-8.tar.gz", repos=NULL)'

この例ではさらに、image_magix.pyを使用し、JPEGイメージ入力を操作します(このイメージで試すことができます)。これらのファイルは、次のスクリプトが配置され、実行されるものと同じフォルダにある必要があります。

import polyglot
import sys
import time
sys.path.insert(0, ".")
from image_magix import Image

load_jpeg = polyglot.eval(string="""function(file.name) {
    library(jpeg)
    jimg <- readJPEG(file.name)
    jimg <- jimg*255
    jimg
}""", language="R")

raw_data = load_jpeg("python_demo_picture.jpg")

# the dimensions are R attributes; define function to access them
getDim = polyglot.eval(string="function(v, pos) dim(v)[[pos]]", language="R")

# Create object of Python class 'Image' with loaded JPEG data
image = Image(getDim(raw_data, 2), getDim(raw_data, 1), raw_data)

# Run Sobel filter
result = image.sobel()

draw = polyglot.eval(string="""function(processedImgObj) {
    require(grDevices)
    require(grid)
    mx <- matrix(processedImgObj$`@data`/255, nrow=processedImgObj$`@height`, ncol=processedImgObj$`@width`)
    grDevices:::awt()
    grid.raster(mx, height=unit(nrow(mx),"points"))
}""", language="R")

draw(result)
time.sleep(10)

Javaホスト相互運用性API

最後に、Javaと相互運用するために(JVM上で実行している場合のみ)、javaモジュールを使用できます:

import java
BigInteger = java.type("java.math.BigInteger")
myBigInt = BigInteger(42)
myBigInt.shiftLeft(128)
# public Java methods can just be called
myBigInt["not"]()
# Java method names that are keywords in Python can be accessed using "[]"
byteArray = myBigInt.toByteArray()
# Java arrays can act like Python lists
print(list(byteArray))

javaパッケージの下のパッケージについては、通常のPythonインポート構文を使用することもできます:

import java.util.ArrayList
from java.util import ArrayList

java.util.ArrayList == ArrayList

al = ArrayList()
al.add(1)
al.add(12)
print(al)
# prints [1, 12]

type組込みメソッドに加えて、javaモジュールによって次のメソッドも公開されます:

組込み 仕様
instanceof(obj, class) objclassのインスタンスである場合、Trueを返します(classは外部オブジェクト・クラスである必要があります)
is_function(obj) objが、Truffle Interopを使用してラップされたJavaホスト言語関数である場合、Trueを返します
is_object(obj) obj引数が、Truffle Interopを使用してラップされたJavaホスト言語オブジェクトである場合、Trueを返します
is_symbol(obj) obj引数が、java.typeによって取得されたJavaクラスのコンストラクタおよび静的メンバーを表すJavaホスト・シンボルである場合、Trueを返します
import java
ArrayList = java.type('java.util.ArrayList')
my_list = ArrayList()
print(java.is_symbol(ArrayList))
# prints True
print(java.is_symbol(my_list))
# prints False, my_list is not a Java host symbol
print(java.is_object(ArrayList))
# prints True, symbols are also host objects
print(java.is_function(my_list.add))
# prints True, the add method of ArrayList
print(java.instanceof(my_list, ArrayList))
# prints True

他のプログラミング言語との相互運用性の詳細は、『Polyglot Programming』および『Embed Languages』を参照してください。

型の動作

相互運用性プロトコルは、あらゆる種類の方法で重複し、Pythonとの相互運用に制限を与える可能性のある、様々な「型」を定義します。

相互運用性の型からPythonへ

最も重要で明らかなことは、Pythonに渡されるすべての外部オブジェクトがPython型foreignを持つことです。つまり、相互運用性のブールであるオブジェクトをPython型boolにするエミュレーションはありません。これは、相互運用性の型の重複がPythonの組込み型では不可能な場合があり、何を優先するかが明確でなくなるためです。かわりに、foreign型が、インタプリタ全体で使用される型変換のためのすべてのPython特殊メソッド(__add____int____str____getitem__などのメソッド)を定義します。これらが、相互運用性の型に基づいて適切な処理を試行します(または例外を発生させます)。

次の表に示されていない型は、現時点ではPythonでの特別な解釈はありません。

相互運用性の型 Pythonの解釈
Null これはNoneと似ています。重要事項: 相互運用性のnull値は等価ですが、同一ではありません。これは、JavaScriptが、undefinednullというnullに似た2つの値(同一ではない)を定義しているために行われました。
Boolean Pythonではすべてのブールは整数でもあるという事実を含め、Pythonのブールと同様に動作します(trueの場合は1、falseの場合は0)
番号 Pythonの数値と同様に動作します。Pythonには1つの整数型と1つの浮動小数点型しかありませんが、型付き配列など一部の場所では範囲が考慮されます。
String Pythonの文字列と同様に動作します。
Buffer Bufferは、PythonのネイティブAPIの概念でもあります(若干異なります)。相互運用性のバッファは、データのコピーを回避するために、一部の場所(memoryviewなど)ではPythonのバッファと同様に扱われます。
Array Arrayは、Pythonのリストのような添字アクセスで、整数およびスライスをインデックスとして使用できます。
Hash Hashは、Pythonのディクショナリのような添字アクセスで、ハッシュ可能な種類のオブジェクトをキーとして使用できます。hashableはPythonセマンティクスに従っており、一般に、アイデンティティを持つすべての相互運用性型はhashableとみなされます。相互運用性オブジェクトがArrayとHashの両方である場合、添字アクセスの動作は未定義です。
Members Membersは、通常のPythonの「.」表記またはgetattrなどの関数を使用して読み取ることができます。
Iterable Iterableは、__iter__メソッドを使用したPythonオブジェクトと同様に扱われます。つまり、ループや、Python反復可能オブジェクトを受け入れるその他の場所で使用できます。
Iterator Iteratorは、__next__メソッドを使用したPythonオブジェクトと同様に扱われます。
Exception 相互運用性の例外は、汎用のexcept句で捕捉できます。
MetaObject 相互運用性のメタ・オブジェクトは、subtypeおよびisinstanceチェックで使用できます
Executable Executableオブジェクトは関数として実行できますが、キーワード引数とともに実行することはできません。
Instantiable Instantiableオブジェクトは、実行可能オブジェクトと同様に動作します(Pythonでの処理方法と同様)。

Pythonから相互運用性の型へ

相互運用性の型 Pythonの解釈
Null Noneのみ。
Boolean Python boolのサブタイプのみ。Pythonセマンティクスとは対照的に、Python boolが同時に相互運用性の数値になることはありません
番号 intおよびfloatのサブタイプのみ。
String strのサブタイプのみ。
Array __getitem__および__len__を持つ任意のオブジェクト。ただし、keysvaluesおよびitemsも持っている場合は該当しません(dictと同様)。
Hash dictのサブタイプのみ。
Members 任意のPythonオブジェクト。readable/writableのルールは、Python MOPの一部でないことを確認するため、若干非定型です。
Iterable __iter__メソッドまたは__getitem__メソッドを持つ任意のもの。
Iterator __next__メソッドを持つ任意のもの。
Exception 任意のPython BaseExceptionサブタイプ。
MetaObject 任意のPython type
Executable __call__メソッドを持つ任意のもの。
Instantiable 任意のPython type