5

別のモジュールのサブディレクトリに実際にあるPythonモジュールをインポートできるようにしたいと思います。

プラグインを使ったフレームワークを開発しています。数千(現在はすでに250を超える)が予想され、1000を超えるファイルを含む1つの大きなディレクトリは必要ないため、このようなディレクトリで並べ替えて、名前の最初の文字でグループ化します。 :

framework\
    __init__.py
    framework.py
    tools.py
    plugins\
        __init__.py
        a\
            __init__.py
            atlas.py
            ...
        b\
            __init__.py
            binary.py
            ...
        c\
            __init__.py
            cmake.py
        ...

他のプラグインの開発者や、自分ほど多くのプラグインを必要としない人に負担をかけたくないので、各プラグインを「framework.plugins」名前空間に配置したいと思います。このように、プライベートプラグインの束を追加する人は、それらをframework.pluginsフォルダーに追加するだけで追加でき、次の内容__init__.pyを含むファイルが提供されます。

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

ただし、現在、この設定により、azサブディレクトリも使用するように強制されています。プラグインが別のプラグインを拡張していることがあるので、今は

from framework.plugins.a import atlas

と私はしたい

from framework.pugins import atlas

フルネームスペース名が実際にはフォルダー構造にマップされない名前空間を宣言する方法はありますか?

私はpkg_resourcesパッケージを知っていますが、これはsetuptoolsを介してのみ利用可能であり、余分な依存関係はありません。

import pkg_resources
pkg_resources.declare_namespace(__name__)

ソリューションはPython2.4-2.7.3で動作するはずです

更新:提供された回答を組み合わせて、プラグインからインポートされたすべてのプラグインのリストを取得しようとしました__init__.py。ただし、これは依存関係のために失敗します。'c'フォルダー内のプラグインは、' t'で始まるプラグインをインポートしようとしますが、これはまだ追加されていません。

plugins = [ x[0].find_module(x[1]).load_module(x[1]) for x in pkgutil.walk_packages([ os.path.join(framework.plugins.__path__[0], chr(y)) for y in xrange(ord('a'), ord('z') + 1) ],'framework.plugins.' ) ]

私がここで正しい方向に進んでいるのか、それとも単に物事を複雑にしすぎて、自分のPEP302インポーターを作成したほうがよいのかわかりません。しかし、これらがどのように機能するかについての適切な例を見つけることができないようです。

__getattr__更新:関数を自分でラップするという提案に従おうとしました__init__.pyが、これは役に立たないようです。

import pkgutil
import os
import sys
plugins = [x[1] for x in pkgutil.walk_packages([ os.path.join(__path__[0], chr(y)) for y in xrange(ord('a'), ord('z') + 1) ] )]
import types
class MyWrapper(types.ModuleType):
    def __init__(self, wrapped):
            self.wrapped = wrapped

    def __getattr__(self, name):
           if name in plugins:
                   askedattr =  name[0] + '.' + name
            else:
                    askedattr = name
            attr = getattr(self.wrapped, askedattr)
            return attr


sys.modules[__name__] = MyWrapper(sys.modules[__name__])
4

4 に答える 4

1

簡単な解決策は、次のようにab...モジュールをインポートすることです。plugins.__init__

from a import atlas
from b import binary
...
于 2012-08-20T15:53:43.197 に答える
1

サブクラス化してオンデマンドtypes.ModuleTypeに変換するmodnameことにより、モジュールを遅延ロードすることをお勧めします。modname[0] + '.' + modnameこれは、モジュールローダーを実装するよりも少ない作業である必要があります。

これを行う方法の例については、apipkgを参照してください。

于 2012-08-21T09:19:57.800 に答える
1

ここでは関数を使用しないpkgutil.extend_pathでください。実行しようとしているのとは逆のことを実行しようとします。

これにより、パッケージ__path__にちなんで名付けられたsys.path上のディレクトリのすべてのサブディレクトリがパッケージに追加されます。これは、単一の論理パッケージのさまざまな部分を複数のディレクトリとして配布する場合に役立ちます。

__path__作品のサブディレクトリを拡張するframework.plugins.__init__.pyだけで問題ありません。

したがって、この問題の解決策は次のとおりです。これをあなたの__init__.py:に入れてください

__path__.extend([os.path.join(__path__[0],chr(y)) for y in range(ord('a'),ord('z')+1)])
于 2012-08-21T16:10:14.057 に答える
0

これは特に高速なソリューション(起動オーバーヘッド)ではありませんが、プラグインを使用する場合はどうでしょうか。__init__ファイルシステムをスクレイプし、見つかった各ファイルをローカル名前空間にインポートしますか?

import glob
import sys

thismodule = sys.modules[__name__]

for plugin in glob.glob("?/*"):
    _temp = __import__(plugin.split("/")[0],
                   globals(),
                   locals(),
                   [plugin.split("/")[1]],
                   -1)
   setattr(thismodule, plugin.split("/")[1], getattr(_temp, plugin.split("/")[1]))
于 2012-08-20T16:26:04.237 に答える