imp – モジュールのインポート構造に対するインタフェース

目的:imp モジュールは Python の import 文の実装を公開する
利用できるバージョン:2.2.1 以上

imp モジュールにはパッケージやモジュールでコードを読み込む Python のインポート構造における下位実装を公開する機能があります。それは動的にインポートしたモジュールへの1つのアクセスポイントです。そして、コード(例えば、アプリケーションのプラグイン、又は拡張機能)を書くときにインポートする必要のあるモジュール名を知らないときに役立ちます。

example パッケージ

次のサンプルは __init__.py で “example” というパッケージを使用します。

print 'Importing example package'

そして、submodule というモジュールも含みます。

print 'Importing submodule'

パッケージ又はモジュールがインポートされたときに出力する print 文のテキストに注目してください。

モジュールタイプ

Python はモジュールの複数のスタイルをサポートします。モジュールをオープンして名前空間へそのモジュールを追加するときに、モジュールタイプそれぞれの要素独自の操作を必要とします。サポートされているタイプとそのパラメータは get_suffixes() 関数で表示することができます。

import imp

module_types = { imp.PY_SOURCE:   'source',
                 imp.PY_COMPILED: 'compiled',
                 imp.C_EXTENSION: 'extension',
                 imp.PY_RESOURCE: 'resource',
                 imp.PKG_DIRECTORY: 'package',
                 }

def main():
    fmt = '%10s %10s %10s'
    print fmt % ('Extension', 'Mode', 'Type')
    print '-' * 32
    for extension, mode, module_type in imp.get_suffixes():
        print fmt % (extension, mode, module_types[module_type])

if __name__ == '__main__':
    main()

get_suffixes() はファイル拡張子、ファイルを開くために使用するモード、そしてモジュールで定義された定数のタイプコードを含むタプルのリストを返します。このテーブルは不完全です。というのは、インポート可能なモジュール又はパッケージのタイプは1つのファイルに対応しないからです。

$ python imp_get_suffixes.py
 Extension       Mode       Type
--------------------------------
       .so         rb  extension
 module.so         rb  extension
       .py          U     source
      .pyc         rb   compiled

モジュールを見つける

モジュールを読み込むための最初のステップはそのモジュールを見つけることです。 find_module() はインポートされた検索パスを精査して、与えられた名前からパッケージ又はモジュールを探します。 find_module() は(そのタイプが適切なら)ファイルハンドラ、モジュールを見つけたファイル名、そして “description”( get_suffixes() が返すタプル) を返します。

import imp
from imp_get_suffixes import module_types

print 'Package:'
f, filename, description = imp.find_module('example')
print module_types[description[2]], filename
print

print 'Sub-module:'
f, filename, description = imp.find_module('submodule', [filename])
print module_types[description[2]], filename
if f: f.close()

find_module() はドットを含むパッケージ名(“example.submodule”)には注意しません。そのため、呼び出し側はネストされたモジュールの正しいパスを渡すことに注意する必要があります。パッケージからサブモジュールをインポートする場合、探しているモジュールを配置するために find_module() にパッケージのディレクトリパスを渡す必要があります。

$ python imp_find_module.py
Package:
package /Users/dhellmann/Devel/pymotw-ja/t2y/PyMOTW/imp/example

Sub-module:
source /Users/dhellmann/Devel/pymotw-ja/t2y/PyMOTW/imp/example/submodule.py

もし find_module() がそのモジュールを配置できなかったら ImportError を発生させます。

import imp

try:
    imp.find_module('no_such_module')
except ImportError, err:
    print 'ImportError:', err
$ python imp_find_module_error.py
ImportError: No module named no_such_module

モジュールを読み込む

モジュールを見つけたら、実際にそのモジュールをインポートするために load_module() を使用してください。 load_module() は完全にドットで区切られたパスのモジュール名と find_module() が返す値(ファイルハンドラ、ファイル名、説明のタプル)を引数に取ります。

import imp

f, filename, description = imp.find_module('example')
example_package = imp.load_module('example', f, filename, description)
print 'Package:', example_package

f, filename, description = imp.find_module('submodule', 
                                           example_package.__path__)
try:
    submodule = imp.load_module('example.module', f, filename, description)
    print 'Sub-module:', submodule
finally:
    f.close()

load_module() は与えられた名前でモジュールのコードを読み込んで新たなモジュールオブジェクトを作成します。そして sys.modules にモジュールを追加します。

$ python imp_load_module.py
Importing example package
Package: <module 'example' from '/Users/dhellmann/Documents/PyMOTW/trunk/PyMOTW/imp/example/__init__.py'>
Importing submodule
Sub-module: <module 'example.module' from '/Users/dhellmann/Documents/PyMOTW/trunk/PyMOTW/imp/example/submodule.py'>

既にインポートされているモジュールに対して load_module() を呼び出すと、既存のモジュールオブジェクトに reload() を呼び出すような結果になります。

import imp
import sys

for i in range(2):
    print i,
    try:
        m = sys.modules['example']
    except KeyError:
        print '(not in sys.modules)',
    else:
        print '(have in sys.modules)',
    f, filename, description = imp.find_module('example')
    example_package = imp.load_module('example', f, filename, description)

新たなモジュールを作成する代わりに、既存モジュールのコンテンツを単純に置き換えます。

$ python imp_load_module_reload.py
0 (not in sys.modules) Importing example package
1 (have in sys.modules) Importing example package

See also

imp
本モジュールの標準ライブラリドキュメント
Modules and Imports
インポートフック、モジュールのパス検索、その他の関連する仕組み
inspect
モジュールプログラムから情報を読み込む
PEP 302
新たなインポートフック
PEP 369
インポートフックの投稿
Bookmark and Share