улучшение трассировки стека в Python - PullRequest
3 голосов
/ 08 декабря 2010

All

У меня есть вопрос, похожий на вопрос 2617120, найденный здесь:

как использовать traceit для сообщения о входных переменных функций

, где спрашивающий хотел указатели о том, как сделать параметры функции вывода на печать Python, когда они выполнялись через отслеживающий хук.

Я ищу что-то очень похожее на это, но с изюминкой. Вместо того, чтобы выгружать все данные, я хочу eval код при его запуске и распечатывать любые пропущенные переменные. Например, со следующим кодом:

for modname in modnames:                   

if not modname or '.' in modname:      
     continue                                                                    
...                                  

В результате трассировки будет напечатано следующее:

for modname in modnames:                | for init in init,., encoding
                                        |
if not modname or '.' in modname:       | if not init or '.' in init
     continue                           |     continue
if not modname or '.' in modname:       | if not . or '.' in .
...                                     |

, где строка кода подвергается интерполяции на основе бегущего кадра. Я сделал это в Perl, где в определенных обстоятельствах это спасатель.

У кого-нибудь есть идеи о том, как лучше всего это сделать в python? У меня есть свои идеи, но я хотел бы услышать, что люди думают (и если у них есть какие-то уже готовые решения)

Здесь, кстати, код ссылки:

import sys
import linecache
import random

def traceit(frame, event, arg):
    if event == "line":
        lineno = frame.f_lineno
        filename = frame.f_globals["__file__"]
        if filename == "<stdin>":
            filename = "traceit.py"
        if (filename.endswith(".pyc") or
            filename.endswith(".pyo")):
            filename = filename[:-1]
        name = frame.f_globals["__name__"]
        line = linecache.getline(filename, lineno)
        print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name,line.rstrip())
    return traceit


def main():
    print "In main"
    for i in range(5):
        print i, random.randrange(0, 10)
    print "Done."

sys.settrace(traceit)
main()

1 Ответ

0 голосов
/ 08 декабря 2010

Вот быстрый взлом, который может дать вам некоторое начало, учитывая строку текста в line и текущий кадр стека в frame (это, кстати, внутренняя функция traceit) ).

import re
from types import *

def interpolatevar(matchobj):
    excludetypes = set((NoneType, TypeType, FunctionType, LambdaType, ClassType,
                    CodeType, InstanceType, MethodType, BuiltinFunctionType,
                    BuiltinMethodType))

    var = matchobj.group(0)
    basevar = var.split(".")[0]
    if basevar in frame.f_code.co_names or basevar in frame.f_code.co_varnames:
        if basevar in frame.f_globals or basevar in frame.f_locals:
            val = eval(var, frame.f_globals, frame.f_locals)
            if type(val) not in excludetypes:
                return repr(val)
    return var

line = re.sub(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*", 
              interpolatevar, line)

Поскольку для поиска идентификаторов используется регулярное выражение, он тупо находит их, даже если они в строковых литералах, но идентификатор должен фактически использоваться в функции и определяться либо в локальной, либо в глобальной области.

Он обрабатывает доступ к атрибутам объекта (например, foo.bar будет заменен этим значением), чего не сделала бы версия, которую я первоначально разместил, и отфильтровывает значения различных типов, так как замена foo на <function foo at 0x02793470> didn ' Я действительно многого тебе не скажу. (Конечно, список исключений легко настраивается, если вас интересуют некоторые значения этих типов или вы можете добавить другие из модуля types.)

В долгосрочной перспективе я думаю, что было бы выгодно взглянуть на байт-код для строки, так как легче определить, какие токены на самом деле являются идентификаторами. Модуль ast также может быть полезен для создания дерева разбора оператора, которое позволит вам выяснить, что такое идентификаторы, но это проблематично для условий и циклов, когда вы видите только строку за раз.

...