Pythonコードの解析とpycファイル
このガイドでは、PythonファイルがGraalVM Pythonランタイムでどのように解析されるかについて詳しく説明します。
pycファイルの作成および管理
目的の.pyファイルと一致する.pycファイルが見つからないか、無効なファイルが見つかった場合、.pycファイルはGraalVM Pythonランタイムによって自動的に作成されます。
Pythonソース・ファイル(モジュール)が最初の実行中にインポートされたときに、適切な.pycファイルが自動的に作成されます。同じモジュールが再度インポートされたときには、すでに作成されている.pycファイルが使用されます。つまり、まだ実行(インポート)されていないソース・ファイルについては.pycファイルはありません。.pycファイルの作成は完全にFileSystem APIを介して行われ、埋込み担当者はファイルシステム・アクセスを管理できます。
その後、スクリプトを実行するたびに、既存の.pycファイルが再利用されるか、新しいものが生成されます。元のソース・ファイルのタイムスタンプまたはハッシュコードが変更されると、.pycファイルが再生成されます。ハッシュコードは、source.hashCode()をコールすることによってPythonソースに基づいてのみ生成され、java.util.Arrays.hashCode(byte[])で計算された、ソース・ファイル・バイトの配列に対するJDKハッシュコードです。
.pycファイルは、Pythonパーサーのマジック番号が変更された場合にも再生成されます。マジック番号はPythonソースでハードコードされており、ユーザーが変更することはできません(当然ながら、そのユーザーがPythonのバイトコードへのアクセス権を持っている場合を除きます)。
GraalVMのPythonランタイムの開発者は、バイトコード形式が変更されたときにマジック番号を変更します。これは実装の詳細であるため、マジック番号はGraalVMのPythonランタイムのバージョンに対応している必要はありません(CPythonの場合と同様)。pycのマジック番号は、実行されている具体的なPythonランタイムJavaコードに応じて変わります。
.pycファイルを使用する場合、バージョンを切り替えるときまたは元のソース・コードを変更するときには少なくともGraalVMのPythonランタイムへの書込みアクセスを許可する必要があります。そうしないと、ソース・ファイルの再生成が失敗し、インポートのたびに、古い.pycファイルへのアクセス、コードの解析、そのシリアライズ、および新しい.pycファイルの記述の試行(および失敗)というオーバーヘッドが発生します。
*.pycファイルは、GraalVMのPythonランタイムによって削除されることはなく、再生成されるのみです。これは、該当するソース・ファイルが変更されたとき(内容の最終変更のタイムスタンプまたはハッシュコード)、またはPython実装パーサーのマジック番号が変更されたときに再生成されます。マジック番号の変更はリリース・ノートで通知されるため、埋込み担当者またはシステム管理者はアップグレード時に古い.pycファイルを削除できます。
.pycファイルについて作成されるフォルダ構造は次のようになります:
top_folder
__pycache__
sourceA.graalpy.pyc
sourceB.graalpy.pyc
sourceA.py
sourceB.py
sub_folder
__pycache__
sourceX.graalpy.pyc
sourceX.py
デフォルトでは、__pycache__ディレクトリはソース・コード・ファイルと同じディレクトリ・レベルに作成され、このディレクトリに、同じディレクトリのすべての.pycファイルが格納されます。このフォルダには、異なるバージョンのPython (CPythonなどを含む)で作成された.pycファイルが格納されることがあるため、ユーザーには、*.cpython3-6.pycなどで終わるファイルが表示される場合があります。
.pycファイルは主に、CPythonと互換性のある方法でランタイムによって自動的に管理されます。CPythonの場合と同様に、それらの場所と、それらを記述する必要があるかどうかを指定するオプションがあり、これらのオプションの両方をゲスト・コードで変更できます。
*.pycファイルの作成は、CPythonの場合と同じ方法で制御できます(https://docs.python.org/3/using/cmdline.htmlを参照):
- GraalVMのPythonランチャ(
graalpy)は、PYTHONDONTWRITEBYTECODE環境変数を読み取ります。これが空でない文字列に設定されている場合、Pythonはモジュールのインポート時に.pycファイルの記述を試行しません。 - ランチャのコマンドライン・オプション
-Bを指定した場合、前述のものと同じ効果があります。 - ゲスト言語コードは、実行時に
sys組込みモジュールの属性dont_write_bytecodeを変更して、後続のインポートの動作を変更できます。 - ランチャは、
PYTHONPYCACHEPREFIX環境変数を読み取ります。設定すると、接頭辞が指すパスに__pycache__ディレクトリが作成され、ソース・ツリーのディレクトリ構造のミラーがオンデマンドで作成されて.pycファイルが格納されます。 - ゲスト言語コードは、実行時に
sysモジュールの属性pycache_prefixを変更して、後続のインポートの場所を変更できます。
埋込み担当者は、環境変数またはCPythonオプションを使用してこれらのオプションをGraalVMのPythonの実装に通信することはできないため、これらのオプションを次の言語オプションとして使用できます:
python.DontWriteBytecodeFlag--BまたはPYTHONDONTWRITEBYTECODEと同等python.PyCachePrefix-PYTHONPYCACHEPREFIXと同等
Pythonコンテキストでは、.pycファイルの記述がデフォルトで有効になりません。graalpyランチャによってこれがデフォルトで有効になりますが、埋込みユースケースでこれが必要な場合は、__pycache__の場所が適切に管理され、その場所にあるファイルが導出元のソース.pyファイルと同様に操作に対して保護されるように注意を払う必要があります。
また、アプリケーション・ソースをGraalVM EntepriseのPythonランタイムにアップグレードするには、埋込み担当者が必要に応じて古い.pycファイルを削除する必要があります。
セキュリティに関する考慮事項
ファイル操作(データおよびタイムスタンプの取得やpycファイルの記述)はすべて、FileSystem APIを介して行われます。埋込み担当者は、カスタム(読取り専用など)のFileSystem実装を使用して、これらの操作すべてを変更できます。また、埋込み担当者は、GraalVMのPythonランタイムについてI/O権限を無効にすることにより、.pycファイルの作成を事実上無効にすることもできます。
.pycファイルが読取り可能でない場合、その場所は書込み可能ではありません。.pycファイルのシリアライズ・データまたはマジック番号がなんらかの形で破損している場合、デシリアライズは失敗し、単に.pyファイルが再度解析されます。これによって、モジュールの解析についてのみ、パフォーマンスにわずかな影響が生じますが、ほとんどのアプリケーションにとって重大ではありません(アプリケーションがPythonコードのロード以外に実際の作業を行う場合)。