考虑以下代码,test.py
:
import sys, os
import time
import urllib.request
with urllib.request.urlopen('http://www.example.com/') as f:
html = f.read().decode('utf-8')
print("A: {}".format(time.time()))
time.sleep(2)
print("B: {}".format(time.time()))
如果我像往常一样运行,我得到这样的输出:
$ python3 test.py
A: 1634285044.6374931
B: 1634285046.6485002
现在,假设我想跟踪代码-从time.sleep(2)
语句之前开始。我不知道这是怎么做到的,但是一个典型的"追踪"。可以在命令行启动,如下所示:
$ python3 -m trace --trace test.py
--- modulename: test, funcname: <module>
test.py(1): import sys, os
test.py(2): import time
test.py(3): import urllib.request
--- modulename: _bootstrap, funcname: _find_and_load
<frozen importlib._bootstrap>(1004): --- modulename: _bootstrap, funcname: __init__
<frozen importlib._bootstrap>(153): <frozen importlib._bootstrap>(154): --- modulename: _bootstrap, funcname: __enter__
<frozen importlib._bootstrap>(157): --- modulename: _bootstrap, funcname: _get_module_lock
<frozen importlib._bootstrap>(172): <frozen importlib._bootstrap>(173): <frozen importlib._bootstrap>(174): <frozen importlib._bootstrap>(175): <frozen importlib._bootstrap>(176): <frozen importlib._bootstrap>(177): <frozen importlib._bootstrap>(179): <frozen importlib._bootstrap>(180): <frozen importlib._bootstrap>(183): --- modulename: _bootstrap, funcname: __init__
<frozen importlib._bootstrap>(59): <frozen importlib._bootstrap>(60): <frozen importlib._bootstrap>(61): <frozen importlib._bootstrap>(62): <frozen importlib._bootstrap>(63): <frozen importlib._bootstrap>(64): <frozen importlib._bootstrap>(185): <frozen importlib._bootstrap>(196): <frozen importlib._bootstrap>(198): <frozen importlib._bootstrap>(200): <frozen importlib._bootstrap>(158): --- modulename: _bootstrap, funcname: acquire
...
…但是输出-至少对于进口和urlopen()
-是巨大的,这使得极其很难看到发生了什么!
import urllib.request
和with urllib.request.urlopen ...
行,那么跟踪就是这样的:
$ python3 -m trace --trace test.py
--- modulename: test, funcname: <module>
test.py(1): import sys, os
test.py(2): import time
test.py(9): print("A: {}".format(time.time()))
A: 1634285323.556725
test.py(11): time.sleep(2)
test.py(13): print("B: {}".format(time.time()))
B: 1634285325.5598364
test.py(775): """
所以,理想情况下,我会有一个工具,允许我在代码的中间(或者更确切地说,在任意位置)开始跟踪;下面的伪代码示例:
import sys, os
import time
import urllib.request
with urllib.request.urlopen('http://www.example.com/') as f:
html = f.read().decode('utf-8')
print("A: {}".format(time.time()))
# I'd want the line-by-line tracing to start here; so:
import trace; trace.START_TRACE()
time.sleep(2)
print("B: {}".format(time.time()))
在Python 3中有这样做的工具吗?
是的,所以在@user2357112supportsMonica的评论之后,我查看了trace
模块cpython/trace.py的源代码3.10·python/cpython -我可以看到,它基本上编译了脚本代码(这就是为什么在中间进行干预会很棘手)-但是,它也使用sys.settrace
。
所以,我看了更多,并找到了如何为sys.settrace
制作自定义跟踪器函数的示例-并且可以将OP示例修改为:
import sys, linecache # add
# mytrace function based on https://stackoverflow.com/questions/63927342/limit-data-returned-by-sys-settrace
def mytrace(frame, event, arg_unused): # add
filename = frame.f_code.co_filename
lineno = frame.f_lineno
print((event, filename, lineno, linecache.getline(filename, lineno)) ) # frame.f_locals
return mytrace
import sys, os
import time
import urllib.request
with urllib.request.urlopen('http://www.example.com/') as f:
html = f.read().decode('utf-8')
# START TRACING
# https://stackoverflow.com/questions/65350552/sys-settrace-seems-to-be-ignored-in-threads
# https://stackoverflow.com/questions/55998616/how-to-trace-code-run-in-global-scope-using-sys-settrace
sys._getframe().f_trace = mytrace
sys.settrace(mytrace)
print("A: {}".format(time.time()))
time.sleep(2)
# STOP TRACING
sys.settrace(None)
print("B: {}".format(time.time()))
然后,当你运行这段代码时:
$ python3 test.py
('line', '/tmp/test.py', 23, 'print("A: {}".format(time.time()))n')
A: 1634290271.2226915
('line', '/tmp/test.py', 25, 'time.sleep(2)n')
('line', '/tmp/test.py', 28, 'sys.settrace(None)n')
B: 1634290273.228311
很好-正是我想要的!