別の言語/フレームワークによって提供されるAPIをPythonにマッピングするPythonモジュールの作成に取り組んでいます。理想的には、これをヘルパーメソッドを公開し、他のフレームワークのすべての名前空間をPythonパッケージ/モジュールにマップする単一のルートパッケージとして提示したいと思います。便宜上、CLRを例として取り上げましょう。
import clr.System.Data
import clr.System.Windows.Forms
これは、CLR名前空間とサブパッケージ/サブモジュールclr
を公開する魔法のトップレベルパッケージです(私が見る限り、パッケージは子モジュール/パッケージを持つ単なるモジュールです。他の種類のメンバーが含まれていても有効です)。System.Data
System.Windows.Forms
私はPEP-302meta_path
を読み、カスタムフックをインストールすることで同様の効果を実現する簡単なプロトタイププログラムを作成しました。モジュール自体はclr
適切なPythonモジュールであり、インポートされると、設定され__path__ = []
(パッケージになり、import
サブモジュールのルックアップも試行されます)、フックが登録されます。フック自体は、パッケージのフルネームがで始まるパッケージの読み込みをインターセプトし"clr."
、を使用して新しいモジュールを動的に作成しimp.new_module()
、に登録し、sys.modules
ピクシーダストとレインボーを使用して元のAPIのクラスとメソッドで埋めます。コードは次のとおりです。
clr.py
import sys
import imp
class MyLoader:
def load_module(self, fullname):
try:
return sys.modules[fullname]
except KeyError:
pass
print("--- load ---")
print(fullname)
m = imp.new_module(fullname)
m.__file__ = "clr:" + fullname
m.__path__ = []
m.__loader__ = self
m.speak = lambda: print("I'm " + fullname)
sys.modules.setdefault(fullname, m)
return m
class MyFinder:
def find_module(self, fullname, path = None):
print("--- find ---")
print(fullname)
print(path)
if fullname.startswith("clr."):
return MyLoader()
return None
print("--- init ---")
__path__ = []
sys.meta_path.append(MyFinder())
test.py
import clr.Foo.Bar.Baz
clr.Foo.speak()
clr.Foo.Bar.speak()
clr.Foo.Bar.Baz.speak()
全体として、これはうまく機能しているようです。Pythonは、チェーン内のモジュールが左から右にインポートされることを保証するため、clr
常に最初にインポートされ、チェーンの残りの部分をインポートできるようにするフックを設定します。
しかし、私がここでやっていることはやり過ぎではないかと思います。結局のところ、私はグローバルフックをインストールしています。これは、気にしないものを除外しても、モジュールのインポートのために呼び出されます。おそらく、特定のパッケージからのインポートに対してのみ呼び出され、他のパッケージからは呼び出されないフックをインストールする方法はありますか?それとも、上記はPythonでこの種のことを行う正しい方法ですか?