2011-11-09 70 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)