Песочница/запуск кода 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:

Мой следующий шаг будет выглядеть в 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 и запустить его в режиме отладки...