如何在 Python3 中使用 pyclbr 搜索 __main__ 模块?

问题描述

我想获取代码目录的模块:__main__ 中的所有函数和类:/tmp/rebound/rebound。 当我使用 pyclbr.readmodule_ex API 时:

source_code_data = pyclbr.readmodule_ex(source_code_module,path=source_code_path)

我指定了模块和路径:

DEBUG:root:Source code module: __main__,Source code path: ['/tmp/rebound/rebound/rebound']

然后我收到此错误

File "/Library/Developer/CommandLinetools/Library/Frameworks/python3.framework/Versions/3.8/lib/python3.8/importlib/util.py",line 69,in _find_spec_from_path
    raise ValueError('{}.__spec__ is None'.format(name))
ValueError: __main__.__spec__ is None

然后我尝试使用不应该被公众使用的功能_readmodule

source_code_data = pyclbr._readmodule(source_code_module,source_code_path,)

但我无法决定参数的值应该是什么:inpackage

通过调试器跟踪代码后,我发现了一个错误

def _find_spec_from_path(name,path=None):
    """Return the spec for the specified module.

    First,sys.modules is checked to see if the module was already imported. If
    so,then sys.modules[name].__spec__ is returned. If that happens to be
    set to None,then ValueError is raised. If the module is not in
    sys.modules,then sys.Meta_path is searched for a suitable spec with the
    value of 'path' given to the finders. None is returned if no spec Could
    be found.

    Dotted names do not have their parent packages implicitly imported. You will
    most likely need to explicitly import all parent packages in the proper
    order for a submodule to get the correct spec.

    """
    if name not in sys.modules:
        return _find_spec(name,path)
    else:
        module = sys.modules[name]
        if module is None:
            return None
        try:
            spec = module.__spec__
        except AttributeError:
            raise ValueError('{}.__spec__ is not set'.format(name)) from None
        else:
            if spec is None:
                raise ValueError('{}.__spec__ is None'.format(name))
            return spec

这是模块中的函数python3.8/importlib/util.py,当它落在 __main__ 块中时,它会将 else 评估为内置模块。

如何区分要读取的目标源代码__main__ 和内置的 __main__?换句话说,我如何读取代码库的模块__main__:反弹?

解决方法

TL:DR

试试:

source_code_data = pyclbr.readmodule_ex("rebound.__main__",path=source_code_path)

说明

如您所知:_find_spec_from_path 将在 name 中搜索 sys.modules__main__ 始终存在。

如果您检查 sys.modules.keys(),您会注意到它包含点分隔的模块名称。 来自 Ipython shell 的示例:

 'IPython.display','IPython.extensions','IPython.extensions.storemagic','IPython.lib','IPython.lib.backgroundjobs','IPython.lib.clipboard','IPython.lib.display','IPython.lib.pretty','IPython.lib.security','IPython.paths',

如果您意识到您正在寻找 rebound.__main__ 而不是 __main__,这很明显。为了进入 if 块,name 不能在 sys.modules 中。最后一句是 _find_spec_from_path 没有错误。

# python3.8/importlib/util.py
def _find_spec_from_path(name,path=None):
    # ...
    if name not in sys.modules:
        return _find_spec(name,path)
    else:
    #...