Хорошо, ребята, я немного продвинулся.
Скажем, у нас есть такой исходный файл, мы хотим запустить оператор за оператором:
print("single line")
for i in xrange(3):
print(i)
print("BUG, executed outside for-scope, so only run once")
if i < 0:
print("Should not get in here")
if i > 0:
print("Should get in here though")
Я хочу выполнять его по одному выражению за раз, имея доступ к локальным / глобальным сообщениям. Это быстрое грязное доказательство концепции (не обращая внимания на ошибки и грубость):
# returns matched text if found
def re_match(regex, text):
m = regex.match(text)
if m: return m.groups()[0]
# regex patterns
newline = "\n"
indent = "[ ]{4}"
line = "[\w \"\'().,=<>-]*[^:]"
block = "%s:%s%s%s" % (line, newline, indent, line)
indent_re = re.compile(r"^%s(%s)$" % (indent, line))
block_re = re.compile(r"^(%s)$" % block)
line_re = re.compile(r"^(%s)$" % (line))
buf = ""
indent = False
# parse the source using the regex-patterns
for l in source.split(newline):
buf += l + newline # add the newline we removed by splitting
m = re_match(indent_re, buf) # is the line indented?
if m:
indent = True # yes it is
else:
if indent: # else, were we indented previously?
indent = False # okay, now we aren't
m = re_match(block_re, buf) # are we starting a block ?
if m:
indent = True
exec(m)
buf = ""
else:
if indent: buf = buf[4:] # hack to remove indentation before exec'ing
m = re_match(line_re, buf) # single line statement then?
if m:
exec(m) # execute the buffer, reset it and start parsing
buf = ""
# else no match! add a line more to the buffer and try again
Выход:
morten@laptop /tmp $ python p.py
single line
0
1
2
BUG, executed outside for-scope, son only run once
Should get in here though
Так что это то, чего я хочу. Этот код разбивает источник на исполняемые операторы, и я могу «делать паузу» между операторами и манипулировать средой. Как показывает код выше, я не могу понять, как правильно разбить код и выполнить его снова. Это заставило меня подумать, что я должен иметь возможность использовать какой-то инструмент для разбора кода и запуска его так, как я хочу.
Прямо сейчас я думаю ast
или pdb
, как вы, ребята, предлагаете.
Беглый взгляд показывает, что ast
может сделать это, но это кажется немного сложным, поэтому мне придется копаться в документации. Если pdb
может управлять потоком программным путем, это тоже может быть ответом.
Обновление:
Ооо, я еще кое-что прочитал и нашел эту тему: Какие крутые хаки можно сделать с помощью sys.settrace?
Я изучил использование sys.settrace()
, но, похоже, это не тот путь. Я все больше и больше убеждаюсь в том, что мне нужно использовать модуль ast
, чтобы получить настолько хороший контроль, насколько мне хотелось бы. Вот код, который нужно использовать settrace()
для пиковых значений внутри области действия функции:
import sys
def trace_func(frame,event,arg):
print "trace locals:"
for l in frame.f_locals:
print "\t%s = %s" % (l, frame.f_locals[l])
def dummy(ls):
for l in ls: pass
sys.settrace(trace_func)
x = 5
dummy([1, 2, 3])
print "whatisthisidonteven-"
выход:
morten@laptop /tmp $ python t.py
trace locals:
ls = [1, 2, 3]
whatisthisidonteven-
trace locals:
item = <weakref at 0xb78289b4; dead>
selfref = <weakref at 0xb783d02c; to 'WeakSet' at 0xb783a80c>
trace locals:
item = <weakref at 0xb782889c; dead>
selfref = <weakref at 0xb7828504; to 'WeakSet' at 0xb78268ac>
UPDATE:
Хорошо, похоже, я решил это .. :) Я написал простой парсер, который вставляет оператор между каждой строкой кода, а затем выполняет код. Этот оператор является вызовом функции, которая захватывает и сохраняет локальную среду в своем текущее состояние.
Я работаю над текстовым редактором Tkinter с двумя окнами, которые будут делать то же, что и Брет Виктор в своей двоичной демонстрации поиска. Я почти закончил:)