分析Python项目的导入依赖

1 投票
2 回答
1800 浏览
提问于 2025-04-17 05:56

总的来说,我想了解我的项目中使用的大框架到底用到了哪些代码。

首先,我想知道所有的导入(可能通过静态分析来实现),然后如果可以的话,想知道这些导入中哪些实际上是被用到的。

对于第一个问题,我当然可以用正则表达式来解决,但我想找到一种更干净的方法。不过,我不太清楚如何使用ast/inspect/parser来实现。

至于第二个问题,我应该能自动找出哪些导入实际上没有被使用,但我该怎么做呢?

编辑:关于第二个问题,或许最好的办法是用一个简单的导入钩子,它可以记录所有被导入的内容,然后再调用默认的导入机制。

所以我尝试了类似这样的代码:

class MyLoader(object):
"""
Loader object
"""

def __init__(self):
    self.loaded = set()

def find_module(self, module_name, package=None):
    print("requesting %s" % module_name)
    self.loaded.add(module_name)
    return self

def load_module(self, fullname):
    fp, pathname, stuff = imp.find_module(fullname)
    imp.load_module(fullname, fp, pathname, stuff)

但是在尝试导入“random”时,我得到了

from future import division

ImportError: No module named future

我觉得这意味着我缺少了一些东西……我还没有找到任何简单的例子来使用imp进行导入检查,有什么提示吗?

2 个回答

2

我很高兴地说,列出导入的内容其实非常简单。

我需要一个最基本的导入协议实现(这个协议是PEP 302定义的),如果find_module返回None,它就会自动尝试下一个。

这个简单的脚本实际上可以显示传入程序的导入情况:

import sys

class ImportInspector(object):

    def find_module(self, module, path):
        print("importing module %s" % module)


if __name__ == '__main__':
    progname = sys.argv[0]
    # shift by one position
    sys.argv = sys.argv[1:]
    sys.meta_path.append(ImportInspector())

    code = compile(open(progname, 'rb').read(), progname, 'exec')
    exec(code)

有了这个,我们可以在此基础上实现各种技巧。比如,我们可以用一个集合来跟踪所有的导入,并在程序退出时把它们都存储起来。

我觉得我们甚至可以得到导入的层级关系,并生成一个类似于gprof2dot的图,但只是基于对导入的分析。

1

这个分析的问题在于Python的动态特性。实际上,使用的模块可能会依赖于运行时的变量(也就是说,有些模块可能只在特定的运行条件下被导入和使用)。

这可能不是最好的方法,但如果你的代码有相当不错的测试覆盖率,你可以使用coverage.py的输出结果来检查在测试执行过程中加载了哪些模块。

撰写回答