Улучшение трассировки стека в Python
Все,
У меня есть вопрос, похожий на вопрос 2617120, найденный здесь:
где спрашивающий хотел, чтобы указатели о том, как сделать параметры функции печати 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
Модуль также может быть полезен для создания дерева разбора оператора, которое позволит вам выяснить, что такое идентификаторы, но это проблематично для условных выражений и циклов, когда вы видите только строку за раз.