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 フックは以下の順で呼び出されます。

  1. sys.meta_path に登録されているファインダに順にローダーの検索を依頼するため find_module メソッドを呼び出す
  2. 見つかった場合、ファインダはローダーを返す。見つからない場合 None
  3. ローダーの load_module メソッドが呼び出す
  4. モジュールを作成、いくつかの属性を設定。モジュールを 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

インポーターを作成するときに役立つメソッドなどを提供しています。