Песочница/запуск кода python по строкам
Мне бы хотелось сделать что-то вроде этих двух:
Изобретая по принципу @18: 20,
Live ClojureScript Game Editor
Если вы не хотите проверять видео, моя проблема заключается в следующем:
Скажем, у меня был этот код:
....
xs = []
for x in xrange(10):
xs.append(x)
...
Я хотел бы создать среду, где я могу выполнить код, инструкцию для оператора и наблюдать/отслеживать locals/globals по мере их изменения. Возможно, дайте ему список варов, чтобы отслеживать в словарях locals/globals. Подобно прохождению кода и сохранению информации о состоянии.
Оптимально я хотел бы сохранить каждое состояние и связанные с ним контекстные данные (locals/globals), чтобы я мог проверять предикаты для примера.
Я бы хотел сделать что-то вроде примера Binary Victor binarySearch Изобретать по принципу @18: 20
Я понимаю смысл? Мне сложно объяснить текст, но видео показывают, что я хочу попробовать:)
Спасибо за ваше время
Что я пробовал/читал/googled:
-
code.InteractiveConsole
/code.InteractiveInterpreter
- модуль
livecoding
: кажется, работает для чистого функционального/безгодового кода
-
exec
/eval
magic: кажется, что я не могу получить как мелкозернистый контроль, как хотелось бы.
- модуль
trace
тоже не подходит.
- Python eval (компиляция (...), песочница), globals идут в песочнице, если не в def, почему? < - Это близко к тому, что я но он компилирует весь блок строк/кода и запускает его за один шаг. Если бы я мог запускать такой файл, но проверял локали между каждой строкой/оператором.
- запустить исходный код python по строке < - Это не то, что я хочу
- Как Ruby и Python реализуют свои интерактивные консоли? < - В этом разделе предлагается, чтобы я больше смотрел в модуль
code
Мой следующий шаг будет выглядеть в ast
и компилировать код и запускать его поэтапно, но мне действительно нужно какое-то руководство. Должен ли я больше размышлять и inspect
-module??
Я использовал Spin model checker раньше, но он использует свой собственный DSL, и я просто хотел бы сделать моделирование в язык реализации, в данном случае python.
О и BTW Я знаю о проблемах безопасности кода песочницы, но я не пытаюсь создать безопасную среду исполнения, я пытаюсь создать очень интерактивную среду, нацеленную на грубую проверку модели или утверждение предиката, например.
Ответы
Ответ 1
После моего начального успеха с sys.settrace()
я перешел на ast
модуль (абстрактные синтаксические деревья). Я анализирую код, который я хочу проанализировать, а затем вставлять новые вызовы после каждого присваивания для отчета о имени переменной и ее новом значении. Я также вставляю вызовы для отчета о циклах и вызовах функций. Затем я выполняю измененное дерево.
tree = parse(source)
visitor = TraceAssignments()
new_tree = visitor.visit(tree)
fix_missing_locations(new_tree)
code = compile(new_tree, PSEUDO_FILENAME, 'exec')
self.environment[CONTEXT_NAME] = builder
exec code in self.environment
Я работаю над инструментом для живого кодирования, таким как Bret Victor's, и вы можете увидеть мой рабочий код на GitHub, а также некоторые примеры как он ведет себя в тесте. Вы также можете найти ссылки на демонстрационное видео, учебное пособие и загрузки из страницы проекта.
Ответ 2
Обновление:. После моего первоначального успеха с этой техникой я переключился на использование модуля ast
, как описано в моем другом ответе.
sys.settrace()
, кажется, работает очень хорошо. Я взял вопрос hacks, который вы упомянули, и статья Andrew Dalke и получил этот простой пример работа.
import sys
def dump_frame(frame, event, arg):
print '%d: %s' % (frame.f_lineno, event)
for k, v in frame.f_locals.iteritems():
print ' %s = %r' % (k, v)
return dump_frame
def main():
c = 0
for i in range(3):
c += i
print 'final c = %r' % c
sys.settrace(dump_frame)
main()
Мне пришлось решить две проблемы, чтобы заставить это работать.
- Функция трассировки должна возвращать себя или другую функцию трассировки, если вы хотите продолжить трассировку.
- Отслеживание начинается только после первого вызова функции. У меня изначально не было основного метода, и я просто перешел в цикл.
Здесь вывод:
9: call
10: line
11: line
c = 0
12: line
i = 0
c = 0
11: line
i = 0
c = 0
12: line
i = 1
c = 0
11: line
i = 1
c = 1
12: line
i = 2
c = 1
11: line
i = 2
c = 3
14: line
i = 2
c = 3
final c = 3
14: return
i = 2
c = 3
38: call
item = <weakref at 0x7febb692e1b0; dead>
selfref = <weakref at 0x17cc730; to 'WeakSet' at 0x17ce650>
38: call
item = <weakref at 0x7febb692e100; dead>
selfref = <weakref at 0x7febb692e0a8; to 'WeakSet' at 0x7febb6932910>
Ответ 3
Похоже, вам нужна bdb, библиотека отладки python. Он встроен, и документы находятся здесь: http://docs.python.org/library/bdb.html
У него нет всех функций, которые вам кажутся, но это разумное место для его реализации.
Ответ 4
Хорошо, ребята, я немного продвинулся.
Скажем, что у нас есть исходный файл, мы хотим запустить оператор по выражению:
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")
Я хочу выполнить одно выражение за раз, имея доступ к locals/globals. Это быстрое грязное доказательство концепции (не обращайте внимания на ошибки и грубость):
# 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
Вывод:
[email protected] /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
может программно управлять потоком, это тоже может быть ответом.
Update:
Sooo, я прочитал еще немного, и я нашел эту тему: Какие классные хаки можно сделать с помощью sys.settrace?
Я изучил использование sys.settrace()
, но, похоже, это не так. Я все больше убеждаюсь, что мне нужно использовать модуль ast
, чтобы получить как можно более хороший контроль, как я бы хотел. FWIW здесь код, который следует использовать 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-"
выход:
[email protected] /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 с двумя окнами, которые сделают то, что делает Брет Виктор в своей демонстрации binarySearch. Я почти закончил:)
Ответ 5
Для простой трассировки я предлагаю вам использовать pdb. Я нашел это вполне разумным для большинства задач отладки/одиночного шага. Для вашего примера:
import pdb
...
xs = []
pdb.set_trace()
for x in xrange(10):
xs.append(x)
Теперь ваша программа остановится при вызове set_trace()
, и вы можете использовать n
или s
для ввода кода во время его выполнения. AFAIK pdb
использует bdb
как бэкэнд.
Ответ 6
Я вижу, что вы придумали что-то, что работает для вас,
но подумал, что стоит упомянуть "пискриптер".
http://code.google.com/p/pyscripter/
Я новичок в python, но я нахожу это очень полезным для
просто щелкните мимо строки, у которой есть переменная, которую я хочу проверить,
затем нажмите f4, чтобы запустить его в режиме отладчика.
После этого я могу просто навести указатель мыши на переменную, и она всплывает
всплывающая подсказка с переменными значениями.
Вы также можете сделать один шаг через script с f7, как описано здесь:
http://openbookproject.net/thinkcs/python/english3e/functions.html#flow-of-execution
(см. "Следить за потоком выполнения в действии" )
Хотя после того, как я последовал примеру, он по-прежнему шагнул в модуль черепахи.
Ответ 7
загрузить eclipse + pydev и запустить его в режиме отладки...