Разбирать функции 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 файл, чтобы понять, как данные, предоставленные в качестве аргумента этой функции, могут быть сопоставлены с позициями исходного кода, чтобы понять, когда выполняется присваивание.

Когда у меня будет время (возможно, в конце следующей недели), я попытаюсь реализовать что-то на основе этого подхода, чтобы продемонстрировать это.

Другие вопросы по тегам