import フック・importlib
Python 3.1 で導入された importlib は一部が Python 2.7 にバックポートされています。import フックを自分で作成する必要のある人はあまりいませんが、自分の用途に使えるのか調べてみます。(3.1 で導入されましたが、以下は 3.3 で調べたものです。)
PEP 302 [1] およびマニュアルより。
- import をカスタマイズしやすく。これまでは __import__ を置き換えるしかなかった
- Python での import の実装を提供し、拡張などからも利用しやすく
これまでは、import をカスタマイズしようとすると、組み込みの __import__ を独自の実装で置き換えるしかありませんでした。この場合、既存の import で足りる分は __import__ を呼び出したり、それも独自の方法で行うことになります。多くのライブラリがこれを行っており、無駄な労力が費やされていました。
import フック
導入された import フックは以下の順で呼び出されます。
- sys.meta_path に登録されているファインダに順にローダーの検索を依頼するため find_module メソッドを呼び出す
- 見つかった場合、ファインダはローダーを返す。見つからない場合 None
- ローダーの load_module メソッドが呼び出す
- モジュールを作成、いくつかの属性を設定。モジュールを sys.modules に追加、返す。読み込めない場合 ImportError
- (sys.modules に追加せずに読み込めそうな気がするが、現在の実装では load_module メソッドの返り値が利用されておらず、後で sys.modules から検索しなおされるため、追加しなければエラー)
ファインダとローダー
抽象ファインダとローダーは importlib.abc モジュールに定義されています。
空のモジュールを返すごく簡単な例です。
import importlib.abc #import imp import sys class CustomLoader(importlib.abc.Loader): def load_module(self, fullname): #mod = imp.new_module(fullname) # error? mod = type(sys)(fullname) mod.__file__ = "<foo>" mod.__loader__ = self mod.__path__ = fullname sys.modules.setdefault(fullname, mod) return mod class CustomFinder(importlib.abc.Finder): def find_module(self, fullname, path=None): return CustomLoader()
- ファインダは find_module メソッドを実装して、渡される fullname に従ってローダーを返します。ローダーが見つからなかった場合は None を返してください。
- ローダーは fullname に従ってモジュールを作成、属性を設定、sys.modules に追加した後、モジュールを返します。モジュールが作成できなかった場合は ImportError を送出してください。
ローダーの例はドキュメントにはありませんが、PEP 302 の中ほどにあります。
imp.new_module() を呼び出すとグローバル変数 _io が設定されていないエラーが出ました (Python 3.3b1)。module クラスのコンストラクタを呼べばいいようなので、上記のようにしてあります。(imp モジュールは 3.4 から deprecated)
その他のファインダとローダー
importlib.abc モジュールには上記の Loader クラス以外にいくつかの抽象ローダークラスが定義されています。
クラス | 説明 |
---|---|
ResourceLoader | |
InspectLoader | |
ExecutionLoader | |
FileLoader | |
SourceLoader | |
PyLoader | |
PyPycLoader |
importlib.machinery
接頭語やその他のローダーを利用できます。
importlib.util
インポーターを作成するときに役立つメソッドなどを提供しています。