如何阅读使用ModuleFinder找到的模块的__version__?

时间:2018-06-28 20:05:29

标签: python version

我正在使用ModuleFinder来获取我的Python程序中导入的所有模块的列表。我的某些模块中有__version__个数字。如何读取__version__变量?

这是我的测试代码。它不起作用:

__version__ = "1.1.1"

from modulefinder import ModuleFinder
finder = ModuleFinder()
finder.run_script(__file__)
for name,mod in sorted(finder.modules.items()):
    try:
        ver = mod.__version__
    except AttributeError as e:
        ver = '--'
    print(name, ver, mod.__file__)

输出看起来像这样:

$ python3 demo.py|head
__future__ -- /Users/simsong/anaconda3/lib/python3.6/__future__.py
__main__ -- demo.py
_ast -- None
_bisect -- /Users/simsong/anaconda3/lib/python3.6/lib-dynload/_bisect.cpython-36m-darwin.so
_blake2 -- /Users/simsong/anaconda3/lib/python3.6/lib-dynload/_blake2.cpython-36m-darwin.so
_bootlocale -- /Users/simsong/anaconda3/lib/python3.6/_bootlocale.py
_bz2 -- /Users/simsong/anaconda3/lib/python3.6/lib-dynload/_bz2.cpython-36m-darwin.so
_codecs -- None
_collections -- None
_collections_abc -- /Users/simsong/anaconda3/lib/python3.6/_collections_abc.py
...

1 个答案:

答案 0 :(得分:2)

mod不是常规的Python模块对象;它是modulefinder.Module类的实例。但是,它确实具有名为globalnames的字典属性形式的全局名称映射。您可以通过检查映射来验证您的模块具有全局名称__version__

for name, mod in sorted(finder.modules.items()):
    ver = mod.globalnames.get('__version__', '--')
    print(name, ver, mod.__file__)

这将打印1;表示名称存在。这是因为实际模块未加载 ,仅字节码被分析而不执行。

您要么必须实际导入模块,要么进行自己的字节码分析以获取全局名称的 value Module类具有__code__属性,您可以扫描该属性以查看存储__version__时堆栈上的值:

import dis

def load_version_string(codeobj):
    """Returns the constant value loaded for the `__version__` global

    Requires that `__version__` is set from a literal constant value.

    """
    instructions = dis.get_instructions(codeobj)
    for instr in instructions:
        if instr.opname == 'LOAD_CONST':
            nxtop = next(instructions, None)
            if nxtop.opname == 'STORE_NAME' and nxtop.argval == '__version__':
                return instr.argval

然后使用:

for name, mod in sorted(finder.modules.items()):
    ver = '--'
    if '__version__' in mod.globalnames:
        ver = load_version_string(mod.__code__)
    print(name, ver, mod.__file__)

现在__main__的输出更改为显示版本号:

$ python3 demo.py | grep __main__
__main__ 1.1.1 demo.py