进行此类任意跳转的唯一方法是在cpython中,并且以的名称 sys.settrace
。
这是一个来自幽默的goto
愚人节模块的方法,它使我可以跳到(似乎)任意行:
import sys
import inspect
_line_number = None
_frame = None
def jump_to(line_number, frame):
global _line_number, _frame
print("Set jump to line", line_number, "in", inspect.getfile(frame))
_frame = frame
_line_number = line_number
def _trace(frame, event, arg):
global _line_number, _frame
try:
if _line_number is not None:
if inspect.getfile(_frame) == inspect.getfile(frame):
print("Jumping to line", _line_number, "in", inspect.getfile(frame))
frame.f_lineno = _line_number
_line_number = None
except ValueError as e:
print(e)
return _trace
def install():
sys.settrace(_trace)
frame = sys._getframe().f_back
while frame:
frame.f_trace = _trace
frame = frame.f_back
如果我这样运行:
import traceh
traceh.install()
import inspect
traceh.jump_to(10, inspect.currentframe())
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)
我得到令人兴奋的输出:
Set jump to line 10 in tr.py
Jumping to line 10 in tr.py
4
5
6
现在,可以sys.excepthook
肯定地将其嵌入吗?
...
def new_sys_excepthook(type, value, traceback):
if type == NameError:
jump_to(traceback.tb_lineno, traceback.tb_frame)
traceback.tb_frame
return
sys.__excepthook__(type, value, traceback)
def install():
sys.excepthook = new_sys_excepthook
sys.settrace(_trace)
...
并使用它:
import traceh
traceh.install()
raise NameError
print(5)
print(6)
和输出…
Set jump to line 4 in tr.py
问题很明显:一旦调用sys.excepthook,外部作用域就消失了,因此绝对没有_trace
机会在原始文件中运行!
如果我们 假设 有解决方案,然后再使用jump_to
片刻,该怎么办?
import traceh
traceh.install()
import inspect
try:
raise NameError
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)
except:
traceh.jump_to(10, inspect.currentframe())
这可以避免我们上次遇到的问题,因为我们正在jump_to
文件内部手动调用。让我们来看看:
Set jump to line 10 in tr.py
Jumping to line 10 in tr.py
can't jump into the middle of a block
该死的
Richie Hindle将想法功劳归功于该goto
模块。