生成导入的图表

5
我即将达到我的最终目标,那就是生成一个漂亮的图表,展示模块与其他导入的模块之间的关系。
例如,如果x从y和z导入,而y又从t和v导入,我希望得到以下结果:
x -> y, z
y -> t, v

现在我已经定义了以下导入钩子,但在简单文件上运行它时,我没有得到我所期望的结果:
python study_imports.py CollectImports simple.py

('study_imports.py', 'study_imports')

simple.py实际上是从study_imports导入的。问题在于我想看到"simple.py"而不是"study_imports.py",有没有办法获取实际导入其他模块的文件路径?

class CollectImports(object):
    """
    Import hook, adds each import request to the loaded set and dumps
    them to file
    """

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

    def __str__(self):
        return str(self.loaded)

    def cleanup(self):
        """Dump the loaded set to file
        """
        dumped_str = '\n'.join(x for x in self.loaded)
        open(self.output_file, 'w').write(dumped_str)

    def find_module(self, module_name, package=None):
        #TODO: try to find the name of the package which is actually
        #importing something else, and how it's doing it
        #use a defualtdict with empty sets as the storage for this job
        entry = (__file__, module_name)
        self.loaded.add(str(entry))

2
你知道 Snakefood 的 sfood-graph 吗? - Sven Marnach
1
我现在很感激,这很好,但我仍然想让我的版本工作,只是为了理解。 - andrea_crotti
3个回答

3
也许可以使用 inspect 模块来实现。
模块 a.py
import inspect

print inspect.stack()

模块 b.py

import a

运行 b.py 时,我遇到了以下错误:
[
   (<frame object at 0x28a9b70>, '/path/a.py', 5, '<module>', ['print inspect.stack()\n'], 0),
   (<frame object at 0x28a9660>, 'b.py', 2, '<module>', ['import to_import\n'], 0)
]

看起来第二个框架包含了你需要的内容。

1

所以我仔细研究了一下snakefood,最终使用AST重写了我的代码。 Snakefood仍然使用编译器,该编译器已经过时且比使用ast慢得多。

结果非常好,例如这是一个访问者:

from ast import parse, NodeVisitor


class ImportVisitor(NodeVisitor):

    def __init__(self):
        self.imported = set()
        super(ImportVisitor, self).__init__()

    def __str__(self):
        return '\n'.join(x for x in self.imported)

    def visit_Import(self, node):
        for n in node.names:
            self.imported.add(n.name)

    #that we are using
    def visit_ImportFrom(self, node):
        self.imported.add(node.module)

这可以作为示例使用:

def gen_module_imports(mod):
    try:
        at = parse(open(mod).read())
    except SyntaxError:
        print("file %s has a syntax error, please fix it" % mod)
        return []
    else:
        v = ImportVisitor()
        v.visit(at)
        return v.imported

0

检查技巧似乎运行良好 :) 在imports.log中,我得到了类似simple.py: set(['study_imports'])的东西。

Class CollectImports(object):
    """
    Import hook, adds each import request to the loaded set and dumps
    them to file
    """

    def __init__(self, output_file):
        self.loaded = defaultdict(lambda: set())
        self.output_file = output_file

    def __str__(self):
        return str(self.loaded)

    def cleanup(self):
        """Dump the loaded set to file
        """
        dumped_str = '\n'.join(('%s: %s' % (k, v)) for k, v in self.loaded.items())
        open(self.output_file, 'w').write(dumped_str)

    def find_module(self, module_name, package=None):
        st = inspect.stack()
        self.loaded[st[1][1]].add(module_name)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接