我正在使用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
...
答案 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