Играя с ast
и compile
(встроенными), кажется, что вы можете использовать NodeTransformer
для изменения некоторых узлов ... Вы также можете редактировать их вручную, если знаете, что ищетеfor.
test.py
print 'Dumb Guy'
x = 4 + 4
print x * 3
change.py
import ast
with open('test.py') as f:
expr = f.read()
e = ast.parse(expr)
e.body[0].values[0].s = 'Cool Guy' # Replace the string
e.body[1].targets[0].id = 'herring' # Change x to herring
e.body[2].values[0].left.id = 'herring' # Change reference to x to reference to herring
c = compile(e, '<string>', 'exec')
exec(c)
Выход change.py:
Крутой парень24
Вы также можете добавить код в тело следующим образом (или заменить элементы обычным способом замены элементов списка):
p = ast.parse('print "Sweet!"', mode='single')
e.body.extend(p)
, а затем просто перекомпилировать и выполнить exec:
c = compile(e, '<string>', 'exec')
exec(c)
Таким способом можно заменить определения функций или отдельные строки.Определение функции будет иметь свое собственное тело, поэтому, если вы добавите некоторую функцию (или цикл), вы сможете получить к ней доступ с помощью
e.body[N].body # Replace N with the index of the FunctionDef object
Однако единственный известный мне способ выполнить один объект ast (_ast.Print
или _ast.Assign
или что-то еще), чтобы сделать что-то вроде этого:
e2 = ast.parse('', mode='exec')
e2.body.append(e.body[0])
exec(compile(e2, '<string>', 'exec'))
, что мне кажется немного хакерским.Что касается строк - каждый объект в AST имеет атрибут lineno
, поэтому, если вы можете извлечь номер строки из исключения, вы можете довольно легко определить, какой оператор вызвал исключение.
Конечно, это на самом деле не решает проблему перемотки стека в состояние предисключения, как вы действительно хотите, похоже.Тем не менее, это может быть возможно сделать через pdb .