2015-05-28 3 views
2

Использование модуля pythons inspect Я выделил объект метода, теперь мне нужно пройти через исходный код метода, чтобы найти вызовы для некоторых других методов и получить их аргументы.Получение вызовов методов и их аргументов из объекта метода

Например, предположим, что в следующем классе:

def my_method(): 
    print('hello') 
    foobar('apple', 'pear', 6) 
    print('world') 
    foobar(1, 2, 3) 
    return foobar('a', 'b') 

Мне нужно извлечь список аргументов, переданных foobar():

[('apple', 'pear', 6), (1, 2, 3), ('a', 'b', None)] 

Можно считать все аргументы жестко и не динамичный.

Учитывая, что объект метода из пакета inspect, как я могу проверить вызовы метода в указанном методе?

Попытка
  • Я попытался с помощью регулярных выражений с inspect.getsourcelines(method), но это ломает, если изменения синтаксиса аргумента.
  • Я изучил абстрактные деревья синтаксиса с помощью модуля pythons ast, но не пришел к какому-либо решению.
  • Должен быть способ заполнить это с помощью inspect, но опять же я не пришел к какому-либо решению.
+0

Я предполагаю, что foo определен где-то? –

+0

'foobar()' да. – thodic

+0

Yep, означало foobar, я изменил код, поэтому вам нужно позвонить в foobar. –

ответ

1

Это не идеально, но должно быть начало, я добавлю более эффективной реализации в бите:

from ast import parse, Call, walk 
import importlib 
import inspect 

mod = "test" 
mod = importlib.import_module(mod) 
p = parse(inspect.getsource(mod)) 

from ast import literal_eval 

vals = [] 
for node in p.body: 
    if isinstance(node, FunctionDef) and node.name == "my_method": 
     for node in walk(node): 
      if isinstance(node,Call) and node.func.id == "foobar": 
       vals.append([literal_eval(val) for val in node.args]) 

print(vals) 

[['apple', 'pear', 6], [1, 2, 3], ['a', 'b']] 

test.py выглядит следующим образом:

def foobar(a=0, b=0, c=None): 
    return a, b, c 

def other_method(x,y,z): 
    return x,y,z 

def my_method(): 
    print('hello') 
    foobar('apple', 'pear', 6) 
    print('world') 
    foobar(1, 2, 3) 
    for i in range(10): 
     if i > 9: 
      s = foobar(4, 5, 6) 
      print(s) 
    return foobar('a', 'b') 


def my_method2(): 
    foobar('orange', 'tomatoe', 6) 
    foobar(10, 20, 30) 
    for i in range(10): 
     if i > 9: 
      foobar(40, 50, 60) 
    other_method("foo","bar","foobar") 
    return foobar('c', 'd') 

Если вы имели смеси обоих вам необходимо будет каким-то образом изменить вызов после print('world') до foobar(a=1, b=2, c=3)

vals = [] 
for node in p.body: 
    if isinstance(node, FunctionDef) and node.name == "my_method": 
     for node in walk(node): 
      if isinstance(node, Call) and node.func.id == "foobar": 
       kws = node.keywords 
       if kws: 
        print("Found keywords",[(kw.arg, literal_eval(kw.value)) for kw in kws]) 
       else: 
        print([literal_eval(val) for val in node.args]) 

Выход:

[['apple', 'pear', 6], [], ['a', 'b'], [4, 5, 6]] 
['apple', 'pear', 6] 
('Found keywords', [('a', 1), ('b', 2), ('c', 3)]) 
['a', 'b'] 
[4, 5, 6] 

Использование ast.Nodevisitor, чтобы найти все объекты, вызов возвратит все вызовы "Foobar" во всех функциях:

from ast import parse, NodeVisitor, literal_eval 
import importlib 
import inspect 

mod = "test" 
mod = importlib.import_module(mod) 
p = parse(inspect.getsource(mod)) 


class FindCall(NodeVisitor): 
    def __init__(self, *args): 
     if len(args) < 1: 
      raise ValueError("Must supply at least ine target function") 
     self.result = {arg: []for arg in args} 

    def visit_Call(self, node): 
     if node.func.id in self.result: 
      self.result[node.func.id].append(map(literal_eval, node.args)) 
     # visit the children 
     self.generic_visit(node) 



fc = FindCall("foobar") 
fc.visit(p) 
print(fc.result) 

Выход:

from pprint import pprint as pp 
pp(fc.result) 

{'foobar': [['apple', 'pear', 6], 
      [1, 2, 3], 
      [4, 5, 6], 
      ['a', 'b'], 
      ['orange', 'tomatoe', 6], 
      [10, 20, 30], 
      [40, 50, 60], 
      ['c', 'd']], 
'other_method': [['foo', 'bar', 'foobar']]} 

Вы можете снова добавить поиск для kwargs и выполнять поиск только определенных функций.

+0

Это фантастика! Он отлично подходит для моего примера и определенно отвечает на мой вопрос. – thodic

+0

Однако я обнаружил недостатки, когда есть более сложный поток, например, если метод содержит цикл for. Я сделаю это еще одним вопросом, если мои собственные эксперименты никуда не денутся, но есть ли у вас какие-нибудь быстрые предложения? – thodic

+1

Я сейчас ушел от моего comp rifgt, но я добавлю способ обработки петель и т. Д. Немного –

Смежные вопросы