私はプラグインモジュールが次のようにロードされるプラグインシステムに取り組んでいます:
def load_plugins():
plugins=glob.glob("plugins/*.py")
instances=[]
for p in plugins:
try:
name=p.split("/")[-1]
name=name.split(".py")[0]
log.debug("Possible plugin: %s", name)
f, file, desc=imp.find_module(name, ["plugins"])
plugin=imp.load_module('plugins.'+name, f, file, desc)
getattr(plugin, "__init__")(log)
instances=instances+plugin.get_instances()
except Exception as e:
log.info("Failed to load plugin: "+str(p))
log.info("Error: %s " % (e))
log.info(traceback.format_exc(e))
return instances
コードは機能しますが、プラグインコードのインポートステートメントごとに、次のような警告が表示されます。
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
メインプログラムコードのエラーは報告されず、プラグインは機能します。
誰かが警告の意味と私が間違っていることを説明できますか? python幸せを保つために、空のプラグインモジュールを個別に作成してインポートする必要がありますか?
ディレクトリplugins
が実際のパッケージ(含まれている__init__.py
fine)、pkgutilsを使用してプラグインファイルを簡単に列挙し、ロードすることができます。
import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))
ただし、とにかくプラグインパッケージがなくても機能します。これを試してください。
import pkgutil
list(pkgutil.iter_modules(["plugins"]))
また、実行時にのみ存在するパッケージを作成することもできます。
import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]
sys.modules["plugins"] = plugins
import plugins.testplugin
しかし、そのハックは主に楽しみのためでした!
プラグインディレクトリに___init__.py
_がない場合、それはパッケージではないため、_plugins.whatever
_を作成すると、Pythonは、そのようなことはすべきではないことを警告します。本当に存在します(パスが何であれ、「_import plugins.whatever
_」では作成できませんでした。)
また、
/
_で分割しないでください。 _os.path.split
_を使用します。.split(".py")
を使用して、拡張子のない名前を取得しないでください。これはバグがあります。 _os.path.splitext
_を使用します。getattr
を使用しないでください。 getattr(plugin, "__init__")
のスペルは_plugin.__init__
_です。__init__
_関数を呼び出す理由がわかりません。これは正しくないようです。おそらく、ロガーを受け取るクラスをインスタンス化するために、「set_logger」関数以上が必要です。L = L + some_other_list
_を使用してリストを拡張するのではなく、extend
メソッドを使用してください。これにより、パフォーマンスが向上し、慣用的になります。except Exception
_によって未知の例外を押しつぶさないでください。例外に応じて何か正気なことをする計画ができない場合、プログラムは正気に進むことができません。ここでの問題は、モジュール名のドット( '。')にあります。
imp.load_module('plugins.'+name, f, file, desc)
'。'を含めないでください'plugins'の後、またはPythonは、モジュールパスであると見なします。
Importステートメントの先頭に以下のステートメントを追加してみてください。
from __future__ import absolute_import
Python imp
ドキュメントは、これが回答されてから更新されました。この問題は、特に find_module()
メソッド。
この関数は、階層モジュール名(ドットを含む名前)を処理しません。 PM、つまり、パッケージのサブモジュール[〜#〜] m [〜#〜]を見つけるには[〜#〜] p [〜# 〜]、
find_module()
およびload_module()
を使用して、パッケージを検索してロードします[〜#〜] p [ 〜#〜]次に、find_module()
を使用し、path引数をP.__path__
に設定します。 [〜#〜] p [〜#〜]自体に点線の名前がある場合は、このレシピを再帰的に適用します。
P.__path__
は、 find_module()
に提供するときに、すでにリストになっていることに注意してください。また、パッケージの検索について find_module()
ドキュメントに記載されている内容にも注意してください。
モジュールがパッケージの場合、fileは
None
、pathnameはパッケージパスであり、descriptionタプルの最後の項目です。PKG_DIRECTORY
です。
したがって、OPの質問から、RuntimeError
警告なしでプラグインをインポートするには、更新されたPythonドキュメント:
# first find and load the package assuming it is in
# the current working directory, '.'
f, file, desc = imp.find_module('plugins', ['.'])
pkg = imp.load_module('plugins', f, file, desc)
# then find the named plugin module using pkg.__path__
# and load the module using the dotted name
f, file, desc = imp.find_module(name, pkg.__path__)
plugin = imp.load_module('plugins.' + name, f, file, desc)