2016-12-22 12 views
6

Próbuję odwzorować zastosowania/przyczyny funkcji i zmiennych w pakiecie Pythona na poziomie funkcji. Istnieje kilka modułów, gdzie funkcje/zmienne są wykorzystywane w innych celach i chciałabym utworzyć słownik, który wygląda mniej więcej tak:Jak znaleźć wszystkie zastosowania funkcji lub zmiennej Pythona w pakiecie Pythona

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

funkcji, które mam na myśli muszą być zdefiniowane w modułach pakiet.

Jak mam zacząć od tego? Wiem, że mogę iterację pakietu __dict__ i test funkcji zdefiniowanych w pakiecie wykonując:

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 

Ale potem muszę znaleźć funkcje wykorzystywane w bieżącej funkcji. Jak to zrobić? Czy jest coś już opracowanego do tego typu pracy? Myślę, że profilowanie bibliotek może wymagać czegoś podobnego.

+1

sprawdź to https://docs.python.org/2/library/ast.html –

+0

Wygląda obiecująco. Podobno są to "brakujące" dokumenty "ast": https://greentreesnakes.readthedocs.io/en/latest/index.html – pbreach

+0

@Ni. Dziękuję za sugestię tego modułu. Skończyło się na tym, że wprowadziłem coś, co zadziałało. – pbreach

Odpowiedz

0

Moduł sugerowany w komentarzach zakończył się pomyślnie. Oto klasa, którą utworzyłem, która służy do wyodrębniania funkcji lub zmiennych zdefiniowanych w pakiecie, które są używane w każdej funkcji.

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 

Klasa może być używany przez pierwszego importu pakiet python i przechodzącej do konstruktora, a następnie wywołanie metody build tak:

import package 

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

Które wydrukować słownik zawierający zestaw kluczy reprezentujący nazwę funkcji oraz wartości, które są listami wskazującymi funkcje i lub zmienne używane w funkcji. Nie każdy przypadek astmy jest brany pod uwagę, ale w moim przypadku było to wystarczająco dobre.

Implementacja rekursywnie dzieli węzły na prostsze typy, aż dotrze do ast.Name, po czym może wyodrębnić nazwę zmiennej, funkcji lub metody, która jest używana w funkcji docelowej.

+0

Przyjmuję teraz moją własną odpowiedź, ponieważ rozwiązała ona mój problem za pomocą modułów "ast" sugerowanych w komentarzach. Wszelkie inne odpowiedzi lub porady są mile widziane. – pbreach

Powiązane problemy