Как печатать функции по мере их вызова
При отладке Python script мне бы очень хотелось узнать весь стек вызовов для всей моей программы. Идеальная ситуация была бы, если бы был флаг командной строки для python, который заставил бы Python печатать все имена функций по мере их вызова (я проверил man Python2.7
, но ничего не нашел в этом роде).
Из-за количества функций в этом script, я бы предпочел не добавлять инструкцию печати к началу каждой функции и/или класса, если это возможно.
промежуточным решением было бы использовать отладчик PyDev, поместить пару контрольных точек и проверить стек вызовов для заданных точек в моей программе, поэтому я буду использовать этот подход в настоящее время.
Я бы предпочел увидеть полный список всех функций, называемых на протяжении всей жизни программы, если такой метод существует.
Ответы
Ответ 1
Вы можете сделать это с помощью функции трассировки (обратитесь к Spacedman для улучшения оригинальной версии этого, чтобы отследить возврат и использовать некоторые хорошие отступы):
def tracefunc(frame, event, arg, indent=[0]):
if event == "call":
indent[0] += 2
print("-" * indent[0] + "> call function", frame.f_code.co_name)
elif event == "return":
print("<" + "-" * indent[0], "exit function", frame.f_code.co_name)
indent[0] -= 2
return tracefunc
import sys
sys.settrace(tracefunc)
main() # or whatever kicks off your script
Обратите внимание, что объект кода функции обычно имеет то же имя, что и связанная функция, но не всегда, поскольку функции могут создаваться динамически. К сожалению, Python не отслеживает объекты функций в стеке (я иногда мечтал представить патч для этого). Тем не менее, это, безусловно, "достаточно хорошо" в большинстве случаев.
Если это становится проблемой, вы можете извлечь "настоящее" имя функции из исходного кода - Python отслеживает имя файла и номер строки - или попросить сборщика мусора выяснить, какой объект функции ссылается на объект кода. Может быть несколько функций, совместно использующих объект кода, но любое из их имен может быть достаточно хорошим.
Возвращаясь к этому вопросу четыре года спустя, я должен упомянуть, что в Python 2.6 и более поздних версиях вы можете добиться большей производительности, используя sys.setprofile()
а не sys.settrace()
. Та же функция трассировки может быть использована; просто функция профиля вызывается только при входе или выходе из функции, так что внутри функции выполняется на полной скорости.
Ответ 2
Еще один хороший инструмент, о котором нужно знать, - это модуль trace:
$ cat foo.py
def foo():
bar()
def bar():
print "in bar!"
foo()
$ python -m trace --listfuncs foo.py
in bar!
functions called:
filename: /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/trace.py, modulename: trace, funcname: _unsettrace
filename: foo.py, modulename: foo, funcname:
filename: foo.py, modulename: foo, funcname: bar
filename: foo.py, modulename: foo, funcname: foo
$python -m trace --trace foo.py
--- modulename: foo, funcname:
foo.py(1): def foo():
foo.py(4): def bar():
foo.py(7): foo()
--- modulename: foo, funcname: foo
foo.py(2): bar()
--- modulename: foo, funcname: bar
foo.py(5): print "in bar!"
in bar!
--- modulename: trace, funcname: _unsettrace
trace.py(80): sys.settrace(None)
Ответ 3
Есть несколько вариантов. Если отладчика недостаточно, вы можете установить функцию трассировки с помощью sys.settrace()
. Эта функция будет по существу вызвана для каждой строки исполняемого кода Python, но легко идентифицировать вызовы функций - см. Связанную документацию.
Вы также можете быть заинтересованы в trace
, хотя он не делает именно то, что вы просили. Обязательно загляните в опцию --trackcalls
.
Ответ 4
import traceback
def foo():
traceback.print_stack()
def bar():
foo()
def car():
bar():
car()
File "<string>", line 1, in <module>
File "C:\Python27\lib\idlelib\run.py", line 97, in main
ret = method(*args, **kwargs)
File "C:\Python27\lib\idlelib\run.py", line 298, in runcode
exec code in self.locals
File "<pyshell#494>", line 1, in <module>
File "<pyshell#493>", line 2, in car
File "<pyshell#490>", line 2, in bar
File "<pyshell#486>", line 2, in foo
проследить
Ответ 5
Вы можете использовать settrace, как описано здесь: Трассировка кода python. Используйте версию в конце страницы. Я вставляю код этой страницы в свой код, чтобы точно увидеть, какие строки выполняются при работе моего кода. Вы также можете фильтровать так, чтобы вы видели только имена вызываемых функций.
Ответ 6
Я взял добрый ответ и построил его.
import sys
WHITE_LIST = ['trade'] # Look for these words in the file path.
EXCLUSIONS = ['<'] # Ignore <listcomp>, etc. in the function name.
def tracefunc(frame, event, arg):
if event == "call":
tracefunc.stack_level += 1
unique_id = frame.f_code.co_filename+str(frame.f_lineno)
if unique_id in tracefunc.memorized:
return
# Part of filename MUST be in white list.
if any(x in frame.f_code.co_filename for x in WHITE_LIST) \
and \
not any(x in frame.f_code.co_name for x in EXCLUSIONS):
if 'self' in frame.f_locals:
class_name = frame.f_locals['self'].__class__.__name__
func_name = class_name + '.' + frame.f_code.co_name
else:
func_name = frame.f_code.co_name
func_name = '{name:->{indent}s}()'.format(
indent=tracefunc.stack_level*2, name=func_name)
txt = '{: <40} # {}, {}'.format(
func_name, frame.f_code.co_filename, frame.f_lineno)
print(txt)
tracefunc.memorized.add(unique_id)
elif event == "return":
tracefunc.stack_level -= 1
tracefunc.memorized = set()
tracefunc.stack_level = 0
sys.setprofile(traceit.tracefunc)
Пример вывода:
API.getFills() # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 331
API._get_req_id() # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1053
API._wait_till_done() # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1026
---API.execDetails() # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1187
-------Fill.__init__() # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 256
--------Price.__init__() # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 237
-deserialize_order_ref() # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 644
--------------------Port() # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 647
API.commissionReport() # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1118
Особенности:
- Игнорирует внутренние функции языка Python.
- Игнорирует повторные вызовы функций (необязательно).
- Использует sys.setprofile() вместо sys.settrace() для скорости.
Ответ 7
Инструмент hunter
делает именно это, и даже больше. Например, учитывая:
test.py:
def foo(x):
print(f'foo({x})')
def bar(x):
foo(x)
bar()
Вывод выглядит так:
$ PYTHONHUNTER='module="__main__"' python test.py
test.py:1 call => <module>()
test.py:1 line def foo(x):
test.py:4 line def bar(x):
test.py:7 line bar('abc')
test.py:4 call => bar(x='abc')
test.py:5 line foo(x)
test.py:1 call => foo(x='abc')
test.py:2 line print(f'foo({x})')
foo(abc)
test.py:2 return <= foo: None
test.py:5 return <= bar: None
test.py:7 return <= <module>: None
Он также предоставляет довольно гибкий синтаксис запроса, который позволяет указывать модуль, файл /lino, функцию и т.д., Что помогает, поскольку вывод по умолчанию (который включает вызовы стандартных библиотечных функций) может быть довольно большим.
Ответ 8
Вы также можете использовать декоратор для определенных функций, которые вы хотите отследить (с их аргументами):
import sys
from functools import wraps
class TraceCalls(object):
""" Use as a decorator on functions that should be traced. Several
functions can be decorated - they will all be indented according
to their call depth.
"""
def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False):
self.stream = stream
self.indent_step = indent_step
self.show_ret = show_ret
# This is a class attribute since we want to share the indentation
# level between different traced functions, in case they call
# each other.
TraceCalls.cur_indent = 0
def __call__(self, fn):
@wraps(fn)
def wrapper(*args, **kwargs):
indent = ' ' * TraceCalls.cur_indent
argstr = ', '.join(
[repr(a) for a in args] +
["%s=%s" % (a, repr(b)) for a, b in kwargs.items()])
self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr))
TraceCalls.cur_indent += self.indent_step
ret = fn(*args, **kwargs)
TraceCalls.cur_indent -= self.indent_step
if self.show_ret:
self.stream.write('%s--> %s\n' % (indent, ret))
return ret
return wrapper
Просто импортируйте этот файл и добавьте @TraceCalls() перед функцией/методом, который вы хотите отследить.
Ответ 9
здесь ответили fooobar.com/info/4696/...
import inspect;print(*['\n\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])