2011-11-09 36 views
0

一般来说,我想了解我的项目中的代码究竟是从大框架实际使用的代码。分析python项目导入

首先我想知道什么是所有的进口(可能与静态分析),然后如果可能的话,实际使用这些进口中的哪一个。

对于第一个问题,我可以使用正则表达式当然,但我想找到一个更清洁的方式。 但我不知道如何与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) 

但是,试图导入“随机”我得到 从未来进口部 导入错误:没有模块名为未来

我想这手段我错过了一些东西.. 我还没有找到任何使用imp来做一些导入反思的简单例子,任何提示?

回答

1

这样的分析问题将会是python的动态特性。实际上,所使用的模块组可能取决于运行时变量(即,某些模块可能仅在某些运行时条件下被导入和使用)。

可能不是最好的方式,但如果你有相当不错的测试覆盖率为您的代码,你可以使用coverage.py输出到检查模块测试执行过程中加载的东西。

+0

是肯定的,有中到底发生了什么静态分析不能很好地工作一个明确的说法。然而,Pylint的确做得相当不错,但我认为它的代码非常繁重。 有关我使用导入器协议的尝试,请参阅上面的内容。 –

1

我很高兴地说,列出进口实际上很简单。

我需要一个最小的实施导入器协议(通过PEP 302所定义的),其中如果find_module返回无它只会后备到下一个的。

这个简单的脚本实际上可以通过显示在通过程序完成的进口:

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) 

鉴于此,任何一种技巧都可以在它上面实现。 例如,我们可以跟踪一组中的导入,并在程序退出时将它们全部存储起来。

我认为我们甚至可能会得到进口hiearchy并产生类似于gprof2dot做什么,但只是基于进口的分析图。

0

不知道我是否应该打开另一个问题,我会尽力在这里。

所以现在我想做一些更花哨,写一个上下文管理器,它确实幕后的肮脏的工作仪器我的代码进行分析。

下面的代码给了我一个很奇怪的错误,我真的不能明白:

File "study_imports.py", line 59, in <module> 
    exec(code) 
File "study_imports.py", line 55, in <module> 
    cl = CollectImports() 
File "study_imports.py", line 15, in __init__ 
    self.loaded = set() 
RuntimeError: maximum recursion depth exceeded while calling a Python object 

import os 
import sys 

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

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

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

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

    def find_module(self, module_name, package=None): 
     self.loaded.add(module_name) 


class CollectorContext(object): 
    """Example of context manager to collect and dump on exit 
    XXX: not working at the moment 
    """ 

    def __init__(self, collector, argv, output_file): 
     self.collector = collector 
     self.argv = argv 
     self.output_file = output_file 

    def __enter__(self): 
     self.argv = self.argv[1:] 
     sys.meta_path.append(self.collector) 

    def __exit__(self, type, value, traceback): 
     # TODO: should assert that the variables are None, otherwise 
     # we are quitting with some exceptions 
     self.collector.dump_to_file(self.output_file) 
     sys.meta_path.remove(self.collector) 



if __name__ == '__main__': 
    # main() 
    progname = sys.argv[0] 
    cl = CollectImports() 

    with CollectorContext(cl, sys.argv, 'imports.log'): 
     code = compile(open(progname).read(), progname, 'exec') 
     exec(code)