在调试Python脚本时,我真的很想知道整个程序的整个调用堆栈。一个理想的情况是,如果有一个python的命令行标志会导致Python在调用时打印所有函数名称(我检查man Python2.7
,但没有找到任何类似的东西)。
由于此脚本中的函数数量,如果可能,我不希望在每个函数和/或类的开头添加print语句。
中间解决方案是使用PyDev的调试器,放置几个断点并检查程序中给定点的调用堆栈,所以我暂时会使用这种方法。
如果存在这样的方法,我仍然希望看到程序生命周期内调用的所有函数的完整列表。
答案 0 :(得分:88)
您可以使用跟踪功能执行此操作(向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()
可以获得更好的性能。可以使用相同的跟踪功能;只是在输入或退出函数时调用配置文件功能,因此函数内部的内容全速执行。
答案 1 :(得分:11)
另一个值得注意的好工具是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)
答案 2 :(得分:7)
有几个选择。如果调试器不够,可以使用sys.settrace()
设置跟踪功能。这个函数基本上会在每行执行的Python代码上调用,但很容易识别函数调用 - 请参阅链接文档。
您可能也对trace
模块感兴趣,但它并不完全符合您的要求。请务必查看--trackcalls
选项。
答案 3 :(得分:5)
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
答案 4 :(得分:2)
您可以使用settrace,如下所述:Tracing python code。使用页面末尾附近的版本。我将该页面的代码粘贴到我的代码中,以确切了解代码运行时执行的行。您也可以进行过滤,以便只看到被调用函数的名称。
答案 5 :(得分:2)
我接受了kindall的回答并以此为基础。
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
功能:
答案 6 :(得分:1)
您还可以将修饰符用于要跟踪的特定功能(及其参数):
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()。
答案 7 :(得分:1)
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
它还提供了一种非常灵活的查询语法,该语法允许指定模块,文件/行号,函数等,这很有帮助,因为默认输出(包括标准库函数调用)可能很大。
答案 8 :(得分:1)
关于kindall答案的变化,仅在程序包中返回被调用的函数。
def tracefunc(frame, event, arg, indent=[0]):
package_name = __name__.split('.')[0]
if event == "call" and (package_name in str(frame)):
indent[0] += 2
print("-" * indent[0] + "> call function", frame.f_code.co_name)
return tracefunc
import sys
sys.settrace(tracefunc)
例如在名为Dog
的程序包中,这应该只向您显示在Dog
程序包中定义的被调用的函数。