5

バックグラウンド

名前空間パッケージを使用し、コードベースを個別のフォルダーに分割すると、pylint がファイルをインポートできないという問題にうんざりしています。そのため、問題の原因として特定されている astNG ソースコードを掘り下げ始めました ( astng のバグレポート8796を参照)。imp.find_module問題の中心にあるのは、インポートを見つけるプロセスで独自の python を使用しているようです。

何が起こるかというと、インポートの最初の (サブ) パッケージ - ain import a.b.c-find_moduleNoneパスが渡されます。戻ってきたパスは、前の例でfind_module見つけようとするルックアップ ループの次のパスに送られます。b

logilab.common.modutils からの疑似コード:

path = None
while import_as_list:
      try:
           _, found_path, etc = find_module(import_as_list[0], path)
      #exception handling and checking for a better version in the .egg files
      path = [found_path]
      import_as_list.pop(0)

問題

これがfind_module問題です。サブパッケージが含まれている場合と含まれていない場合があります。サブパッケージが見つからない場合、バックアウトして次のパッケージを試す方法はありません。

結果をパスリストから削除して2回目の試行を行うことができるように、Noneの代わりにsys.pathを明示的に使用してみましたが、pythonのモジュールファインダーは十分に賢いため、パスに完全に一致する必要はありません、このアプローチを使用できなくします-とにかく私の知る限りでは。

涙目の嘆願

可能なすべての一致を返すか、除外リストを取るfind_modulesの代替手段はありますか? 私はまた、まったく異なるソリューションに対してオープンです。手動で python にパッチを当てないことが望ましいですが、少なくともローカル ソリューションの場合は不可能ではありません。

(注意事項:私はpython 2.6を実行していますが、現在の会社のポリシーがアップグレードできないため、p3kなどの提案は、それが唯一の答えでない限り、承認済みとしてマークされません。)

4

3 に答える 3

4

Python 2.5 以降、これを行う正しい方法はpkgutil.iter_modules() (フラット リストの場合) またはpkgutil.walk_packages() (サブパッケージ ツリーの場合) を使用することです。どちらも名前空間パッケージと完全に互換性があります。

たとえば、「jmb」のサブパッケージ/サブモジュールだけを見つけたい場合は、次のようにします。

import jmb, pkgutil
for (module_loader, name, ispkg) in pkgutil.iter_modules(jmb.__path__, 'jmb.'):
    # 'name' will be 'jmb.foo', 'jmb.bar', etc.
    # 'ispkg' will be true if 'jmb.foo' is a package, false if it's a module

iter_modules または walk_packages を使用して、sys.path 上のすべてのモジュールをウォークすることもできます。詳細については、上記のリンク先のドキュメントを参照してください。

于 2013-02-11T21:09:00.783 に答える
2

PyLint のこの制限にもうんざりしています。

imp.find_modules() に代わるものはわかりませんが、PyLint で名前空間パッケージを処理する別の方法を見つけたと思います。リンク先のバグ レポート (http://www.logilab.org/ticket/8796) に関する私のコメントを参照してください。

アイデアは、pkg_resources を使用して名前空間パッケージを見つけることです。logilab.common.modutils._module_file()の直後の への追加は次のwhile modpathとおりです。

  while modpath:
      if modpath[0] in pkg_resources._namespace_packages and len(modpath) > 1:
          module = sys.modules[modpath.pop(0)]
          path = module.__path__

ただし、これはあまり洗練されておらず、最上位の名前空間パッケージのみを処理します。

于 2012-04-26T15:59:21.460 に答える
0

警告 + 免責事項: まだテストされていません!

前:

for part in parts:
    modpath.append(part)
    curname = '.'.join(modpath)
    # ...
    if module is None:
        mp_file, mp_filename, mp_desc = imp.find_module(part, path)
        module = imp.load_module(curname, mp_file, mp_filename, mp_desc)

後: -言及してくれてありがとうpjebypkgutil !

for part in parts:
    modpath.append(part)
    curname = '.'.join(modpath)
    # ...
    if module is None:
        # + https://stackoverflow.com/a/14820895/611007
        # # mp_file, mp_filename, mp_desc = imp.find_module(part, path)
        # # module = imp.load_module(curname, mp_file, mp_filename, mp_desc)
        import pkgutil
        mp_file = None
        for loadr,name,ispkg in pkgutil.iter_modules(path=path,prefix='.'.join(modpath[:-1])+'.'):
            if name.split('.')[-1] == part:
                if not hasattr(loadr,'path') and hasattr(loadr,'archive'):
                    # with zips `name` was like '.somemodule'
                    # it gives `RuntimeWarning: Parent module '' not found while handling absolute import`
                    # I expect the name I need to be 'somemodule'
                    # TODO: I don't know why python does this or what the correct usage is.
                    # https://stackoverflow.com/questions/2267984/
                    if name and name[0] == '.':
                        name = name[1:]
                    ldr= loadr.find_module(name,loadr.archive)
                    module = ldr.load_module(name)
                    break
                imploader= loadr.find_module(name,loadr.path)
                mp_file,mp_filename,mp_desc= imploader.file,imploader.filename,imploader.etc
                module = imploader.load_module(imploader.fullname)
                break
        if module is None:
            raise ImportError
于 2015-03-30T14:05:22.560 に答える