2016-12-22 16 views
6

Pythonパッケージの関数と変数の使用/原因を関数レベルでマップしようとしています。そこの関数/変数が他の機能に使用されるいくつかのモジュールがあり、私のようなものに見える辞書を作成したい:私はのモジュールで定義する必要があるために参照していますPythonパッケージ内のPython関数または変数のすべての使い方を見つける方法

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

機能をパッケージ。

どうすればこの問題を解決できますか?

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 

をしかし、その後、私は現在の関数内で使用される関数を見つける必要がある:私は私がやっていて、パッケージで定義された機能のためのパッケージ__dict__とテストを繰り返し処理できることを知っています。これはどうすればできますか?このタイプの仕事のためにすでに開発されたものはありますか?私は、ライブラリのプロファイリングがこれに似た何かをしなければならないかもしれないと思う。

+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 

クラスは、最初にそのようにのようなbuildメソッドを呼び出すと、Pythonパッケージをインポートし、コンストラクタに渡すことで使用することができます関数の名前を表す値、および関数で使用される関数および/または変数を示すリストである値を含む。すべてのタイプが説明されているわけではありませんが、私の場合はこれで十分です。

実装は、ast.Nameに達するまで、ノードをより単純な型に再帰的に分解します。その後、ターゲット関数内で使用されている変数、関数、またはメソッドの名前を抽出できます。

+0

コメントで示唆された 'ast'モジュールを使用して私の問題を解決したので、私自身の答えを受け入れてください。その他の回答やアドバイスは高く評価されます。 – pbreach

関連する問題