Улучшение трассировки стека в Python

Все,

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

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

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

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

    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 ответ

    Вот быстрый взлом, который может дать вам некоторое начало, учитывая строку текста в 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> на самом деле не говорит вам много, что вы еще не знаете. (Конечно, список исключений легко настраивается, если вас интересуют некоторые значения этих типов или вы можете добавить другие из types модуль).

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

    Другие вопросы по тегам