Разбирать функции Python как строку в декораторе
Я пытаюсь написать декоратор отладки функции, который будет смотреть на:
def foo(baz):
bar = 1
bar = 2
return bar
и оберните это:
def foo(baz):
bar = 1
print 'bar: {}'.format(bar)
bar = 2
print 'bar: {}'.format(bar)
return bar
Мне нужно поиграть с функцией в виде текста, чтобы получить "\w+(?=\ S *[=])", но я не знаю, как получить к нему доступ. У меня есть декоратор, который я изменил из блога, который работает, но я просто попытался изменить его на:
class decorator_string_check(object):
def __init__(self, func):
self.func = func
wraps(func)(self)
def __call__(self, *args, **kwargs):
print dir(self.func)
print dir(self.func.__code__)
print self.func.__code__.__str__()
ret = self.func(*args, **kwargs)
return ret
@decorator_string_check
def fake(x):
y = 6
y = x
return y
y = fake(9)
и я не получаю ничего ценного, а именно:
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
<code object fake at 0x7f98b8b1d030, file "./logging.py", line 48>
Как мне работать с реальным текстом "func", запускать регулярные выражения и находить вещи, которые мне нужны, внутри объекта класса decorator? Спасибо
1 ответ
Прежде всего, я предлагаю вам не делать что-то подобное. Трудно получить рабочий код и очень трудно сделать правильную версию.
Кроме того, я действительно не знаю, что именно ты хочешь делать. Если этот декоратор добавить print
заявление после каждого назначения и показать обновленное значение? Или отслеживать только определенное подмножество переменных?
Тем не менее, чтобы получить исходный код чего-то, что вы можете использовать inspect
модуль в частности getsource
функция:
In [1]: def test():
...: a = 1
...: b = 2
...:
In [2]: import inspect
In [3]: inspect.getsource(test)
Out[3]: 'def test():\n a = 1\n b = 2\n'
In [4]: print(inspect.getsource(test))
def test():
a = 1
b = 2
Вы можете изменить и проверить исходный код, как вы хотите, и, наконец, compile()
новый исходный код.
Обратите внимание, что:
- Вы должны быть осторожны при изменении исходного кода, потому что легко создать синтаксически неверный код (подумайте: многострочные выражения и т. Д.)
- при компиляции вы хотите компилировать в той же области, что и исходная функция.
inspect
В модуле есть некоторые функции, которые позволяют вам получить стековый фрейм, где вызывается ваш декоратор, и вы можете получить оттуда окружение. Читайте здесь о том, как обращаться со стеком. - Исходный код может быть недоступен. Код может быть скомпилирован в байт-код, а исходный файл может быть удален. В этом случае
getsource
будет просто поднятьOSError
,
Более "разумным" решением было бы не смотреть на исходный код, а на байт-код. Вы можете сделать это с помощью dis
модуль. Вы можете попытаться увидеть, когда значения переменной изменятся, и вставить некоторый байт-код, который будет печатать эту переменную.
Обратите внимание, что dis
модуль был значительно улучшен в python3.4+, поэтому с предыдущими версиями python это было бы сложно.
Вы, вероятно, должны прочитать статьи, такие как взломы байт-кода Python, пересмотренные gotos, прежде чем пытаться это сделать. Они дают вам представление о том, как смотреть на байт-код и как с ним работать.
Это, вероятно, безопаснее (например, даже если исходный файл не существует на компьютере, байт-код все еще будет доступен), тем не менее, я все еще думаю, что то, что вы имеете в виду, не очень хорошая вещь, кроме как в качестве упражнения.
Как указывает jsbueno, правильный способ сделать то, что вы хотите (например, отладчик Python), это использовать sys.settrace
,
Эта функция позволяет вам установить функцию трассировки, которая будет вызываться для каждого выполняемого "кода". Функция будет знать, когда вызывается функция, вводится новый блок и т. Д. Она дает вам доступ к кадру, в котором будет выполняться код, и, таким образом, вы сможете найти интересующие вас значения.
Вы должны проверить lnotab_notes.txt
файл, чтобы понять, как данные, предоставленные в качестве аргумента этой функции, могут быть сопоставлены с позициями исходного кода, чтобы понять, когда выполняется присваивание.
Когда у меня будет время (возможно, в конце следующей недели), я попытаюсь реализовать что-то на основе этого подхода, чтобы продемонстрировать это.