5

背景

当您使用命名空间包并将代码库划分为单独的文件夹时,我已经厌倦了 pylint 无法导入文件的问题。因此,我开始深入研究已被确定为问题根源的 astNG 源代码(参见关于 astng 的错误报告8796)。问题的核心似乎是在imp.find_module查找导入的过程中使用了自己的 python。

发生的情况是导入的第一个(子)包 - ain import a.b.c- 被提供给find_module路径None。无论返回什么路径,都会被送入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,其中可能包含或可能没有您的子包。如果您没有找到子包,则无法退出并尝试下一个。

我尝试显式使用 sys.path 而不是 None,以便可以从路径列表中删除结果并进行第二次尝试,但是 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)

之后:- 感谢pjeby提及pkgutil

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 回答