16

別の言語/フレームワークによって提供されるAPIをPythonにマッピングするPythonモジュールの作成に取り組んでいます。理想的には、これをヘルパーメソッドを公開し、他のフレームワークのすべての名前空間をPythonパッケージ/モジュールにマップする単一のルートパッケージとして提示したいと思います。便宜上、CLRを例として取り上げましょう。

import clr.System.Data
import clr.System.Windows.Forms

これは、CLR名前空間とサブパッケージ/サブモジュールclrを公開する魔法のトップレベルパッケージです(私が見る限り、パッケージは子モジュール/パッケージを持つ単なるモジュールです。他の種類のメンバーが含まれていても有効です)。System.DataSystem.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でこの種のことを行う正しい方法ですか?

4

1 に答える 1

5

一般的に、あなたのアプローチはうまくいくと思います。どのパスを処理するかを指定することが重要なので、「グローバル」であることを心配する必要はありません。このテストをインポートロジック内に移動すると、不必要に複雑になるため、フックの実装者が決定する必要があります。

ちょっとした心配事ですが、多分あなたは使うことができますsys.path_hooksか?それは少し「強力」ではないようですsys.meta_path

sys.path_hooksは呼び出し可能オブジェクトのリストであり、指定されたパス項目を処理できるかどうかを判断するために順番にチェックされます。呼び出し可能オブジェクトは、パス項目という1つの引数で呼び出されます。呼び出し可能オブジェクトは、パスアイテムを処理できない場合はレイズImportErrorし、パスアイテムを処理できる場合はインポーターオブジェクトを返す必要があります。

于 2011-09-01T10:14:26.297 に答える