进行这种任意跳转的唯一方法是在 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
模块。