2016-12-22 59 views
6

我想要在函數級別映射python包中函數和變量的用法/原因。在有些情況下函數/變量在其他功能使用的幾個模塊,我想創建一個字典,看起來像:如何在python包中找到python函數或變量的所有用法

{'function_name':{'uses': [...functions used in this function...], 
        'causes': [...functions that use this function...]}, 
... 
} 

我指給需要的模塊定義的功能包。

我該如何開始呢?我知道我可以做通過包裝__dict__和測試在包中定義的功能迭代:

import package 

import inspect 
import types 

for name, obj in vars(package).items(): 
    if isinstance(obj, types.FunctionType): 
     module, *_ = inspect.getmodule(obj).__name__.split('.') 
     if module == package.__name__: 
      # Now that function is obtained need to find usages or functions used within it 

但在那之後,我需要找到當前函數中使用的功能。如何才能做到這一點?這種類型的工作是否已經開發出來了?我認爲分析庫可能需要做類似的事情。

+1

檢查此https://docs.python.org/2/library/ast.html –

+0

看起來很有希望。顯然這些是「失蹤」的「ast」文檔:https://greentreesnakes.readthedocs.io/en/latest/index.html – pbreach

+0

@Ni。感謝您提出這個模塊。我最終實現了一些實際工作的東西。 – pbreach

回答

0

評論中建議的ast模塊結束了很好的工作。這是我創建的一個類,用於提取在每個函數中使用的包中定義的函數或變量。

import package 

cb = CausalBuilder(package) 
print(cb.build()) 

哪個將打印出含有一組鍵的字典:

import ast 
import types 
import inspect 


class CausalBuilder(ast.NodeVisitor): 

    def __init__(self, package): 
     self.forest = [] 
     self.fnames = [] 

     for name, obj in vars(package).items(): 
      if isinstance(obj, types.ModuleType): 
       with open(obj.__file__) as f: 
        text = f.read() 
       tree = ast.parse(text) 
       self.forest.append(tree) 
      elif isinstance(obj, types.FunctionType): 
       mod, *_ = inspect.getmodule(obj).__name__.split('.') 
       if mod == package.__name__: 
        self.fnames.append(name) 

     self.causes = {n: [] for n in self.fnames} 

    def build(self): 
     for tree in self.forest: 
      self.visit(tree) 
     return self.causes 

    def visit_FunctionDef(self, node): 
     self.generic_visit(node) 
     for b in node.body: 
      if node.name in self.fnames: 
       self.causes[node.name] += self.extract_cause(b) 

    def extract_cause(self, node): 
     nodes = [node] 
     cause = [] 
     while nodes: 
      for i, n in enumerate(nodes): 
       ntype = type(n) 
       if ntype == ast.Name: 
        if n.id in self.fnames: 
         cause.append(n.id) 
       elif ntype in (ast.Assign, ast.AugAssign, ast.Attribute, 
           ast.Subscript, ast.Return): 
        nodes.append(n.value) 
       elif ntype in (ast.If, ast.IfExp): 
        nodes.append(n.test) 
        nodes.extend(n.body) 
        nodes.extend(n.orelse) 
       elif ntype == ast.Compare: 
        nodes.append(n.left) 
        nodes.extend(n.comparators) 
       elif ntype == ast.Call: 
        nodes.append(n.func) 
       elif ntype == ast.BinOp: 
        nodes.append(n.left) 
        nodes.append(n.right) 
       elif ntype == ast.UnaryOp: 
        nodes.append(n.operand) 
       elif ntype == ast.BoolOp: 
        nodes.extend(n.values) 
       elif ntype == ast.Num: 
        pass 
       else: 
        raise TypeError("Node type `{}` not accounted for." 
            .format(ntype)) 

       nodes.pop(nodes.index(n)) 

     return cause 

類可以通過第一導入Python包並傳遞給構造,然後調用build方法,像這樣被使用表示函數的名稱,以及表示函數中使用的函數和/或變量的列表的值。並非每種類型都被考慮在內,但對我而言這已經足夠好了。

該實現遞歸地將節點分解爲更簡單的類型,直到它達到ast.Name之後,它可以提取在目標函數中使用的變量,函數或方法的名稱。

+0

現在接受我自己的答案,因爲它已經使用評論中建議的'ast'模塊解決了我的問題。任何其他答案或建議表示讚賞。 – pbreach

相關問題