Python &引用;“在线”;函数的猴子补丁
您的程序刚刚在Python &引用;“在线”;函数的猴子补丁,python,function,monkeypatching,Python,Function,Monkeypatching,您的程序刚刚在pdb上暂停。set_trace() 是否有办法对当前正在运行的功能进行修补并“恢复”执行? 这可以通过调用帧操作实现吗 一些背景: 通常,我会有一个处理大量数据的复杂函数,而不知道我会找到什么样的数据: def process_a_lot(data_stream): #process a lot of stuff #... data_unit= data_stream.next() if not can_process(data_unit)
pdb上暂停。set_trace()
是否有办法对当前正在运行的功能进行修补并“恢复”执行?
这可以通过调用帧操作实现吗
一些背景: 通常,我会有一个处理大量数据的复杂函数,而不知道我会找到什么样的数据:
def process_a_lot(data_stream):
#process a lot of stuff
#...
data_unit= data_stream.next()
if not can_process(data_unit)
import pdb; pdb.set_trace()
#continue processing
这种方便的构造在遇到未知数据时启动一个交互式调试器,因此我可以随意检查它并更改process\u lot
代码以正确处理它
这里的问题是,当data\u-stream
很大时,您不想再次仔细阅读所有数据(假设next
速度很慢,因此您无法保存已有数据并在下次运行时跳过)
当然,您可以在调试器中随时替换其他函数。您也可以替换函数本身,但它不会更改当前的执行上下文
编辑:
因为有些人被跟踪了:
我知道有很多方法可以构造代码,使处理功能与
process\u a\u lot
分离。我不是在问如何构造代码,而是在代码不准备处理替换时如何恢复(在运行时)。如果我理解正确:
- 你不想重复所有已经完成的工作
- 一旦您了解了如何处理新数据,您需要一种方法来用新代码替换
#继续正常处理
预期的_类型
应该是
data_type:(detect_function, handler_function)
而detect\u type
需要通过它来找到匹配项。如果没有找到匹配项,pdb
弹出,然后您可以了解发生了什么,编写一个新的detect\u函数
和handler\u函数
,将它们添加到预期的\u类型
,并从pdb继续执行
我想知道的是,是否有一种方法可以对当前正在运行的函数(进程)进行修补,并“恢复”执行
因此,您想从pdb内部编写一个新的处理批
函数,然后在pdb
调用的位置将控制权转移给它
或者,是否要在pdb外部重写函数,然后以某种方式从.py
文件重新加载该函数,并将控制权转移到pdb
调用位置的函数中间
我能想到的唯一可能性是:从pdb
中导入新编写的函数,然后用新函数中的字节码替换当前的process\u lot
字节码(我认为它是func.co\u code
之类的)。确保在pdb
行之前的新函数(甚至是pdb
行)中没有任何更改,它可能会工作
但即使是这样,我也会认为这是一个非常脆弱的解决方案。首先是一个(原型)解决方案,然后是一些重要的警告
# process.py
import sys
import pdb
import handlers
def process_unit(data_unit):
global handlers
while True:
try:
data_type = type(data_unit)
handler = handlers.handler[data_type]
handler(data_unit)
return
except KeyError:
print "UNUSUAL DATA: {0!r}". format(data_unit)
print "\n--- INVOKING DEBUGGER ---\n"
pdb.set_trace()
print
print "--- RETURNING FROM DEBUGGER ---\n"
del sys.modules['handlers']
import handlers
print "retrying"
process_unit("this")
process_unit(100)
process_unit(1.04)
process_unit(200)
process_unit(1.05)
process_unit(300)
process_unit(4+3j)
sys.exit(0)
以及:
在Python2.7中,这为您提供了一个字典,将预期/已知类型链接到处理每种类型的函数。如果某个类型没有可用的处理程序,则用户将自己放入调试器中,让他们有机会使用适当的处理程序修改handlers.py
文件。在上面的示例中,没有浮点值
或复杂值的处理程序。当它们出现时,用户必须添加适当的处理程序。例如,可以添加:
def handle_float(x):
print "FIXED FLOAT {0!r}".format(x)
handler[float] = handle_float
然后:
def handle_complex(x):
print "FIXED COMPLEX {0!r}".format(x)
handler[complex] = handle_complex
下面是跑步的样子:
$ python process.py
handle_default: 'this'
handle_default: 100
UNUSUAL DATA: 1.04
--- INVOKING DEBUGGER ---
> /Users/jeunice/pytest/testing/sfix/process.py(18)process_unit()
-> print
(Pdb) continue
--- RETURNING FROM DEBUGGER ---
retrying
FIXED FLOAT 1.04
handle_default: 200
FIXED FLOAT 1.05
handle_default: 300
UNUSUAL DATA: (4+3j)
--- INVOKING DEBUGGER ---
> /Users/jeunice/pytest/testing/sfix/process.py(18)process_unit()
-> print
(Pdb) continue
--- RETURNING FROM DEBUGGER ---
retrying
FIXED COMPLEX (4+3j)
好的,这基本上是可行的。您可以将其改进并调整为更适合生产的形式,使其在Python2和Python3之间兼容,等等
在这样做之前,请仔细考虑。
这种“实时修改代码”的方法是一种非常脆弱的模式和容易出错的方法。它鼓励您在关键时刻进行实时热修复。这些修复可能没有良好或充分的测试。几乎从定义上讲,您刚刚发现您正在处理一种新类型的T。您还不太了解T,它为什么会出现,它的边缘情况和故障模式可能是什么,等等。如果您的“修复”代码或热修补程序不起作用,那会怎么样?当然,您可以进行更多的异常处理,捕获更多的异常类,并可能继续
像这样的Web框架具有基本上以这种方式工作的调试模式。但这些都是调试模式,通常不适合生产。此外,如果在调试器中键入错误的命令,该怎么办?不小心键入“退出”而不是“继续”,整个程序就会结束,随之,您希望继续处理。如果这是用于调试(可能是探索新类型的数据流),请使用
如果这是用于生产使用,而是考虑异步、带外检查和校正,而不是将开发人员/操作员置于实时处理流程的中间。
< P> <强> No.<强>
您不能将当前正在运行的Python函数进行moneky修补,然后继续按下去,就好像什么都没发生过一样。至少不是以任何一般或实际的方式
从理论上讲,这是可能的——但只有在有限的条件下,需要付出很大的努力和高超的技巧。这不能以任何概括性来完成
要进行尝试,您必须:
找到相关函数源并编辑它(简单)
将更改后的函数源编译为字节码(直截了当)
插入新字节码代替旧字节码(可行)
将函数内务管理数据更改为指向程序中的“逻辑”“相同点”,其中
$ python process.py
handle_default: 'this'
handle_default: 100
UNUSUAL DATA: 1.04
--- INVOKING DEBUGGER ---
> /Users/jeunice/pytest/testing/sfix/process.py(18)process_unit()
-> print
(Pdb) continue
--- RETURNING FROM DEBUGGER ---
retrying
FIXED FLOAT 1.04
handle_default: 200
FIXED FLOAT 1.05
handle_default: 300
UNUSUAL DATA: (4+3j)
--- INVOKING DEBUGGER ---
> /Users/jeunice/pytest/testing/sfix/process.py(18)process_unit()
-> print
(Pdb) continue
--- RETURNING FROM DEBUGGER ---
retrying
FIXED COMPLEX (4+3j)
def a(x): LOAD_FAST 0 (x)
y = x + 1 LOAD_CONST 1 (1)
return y BINARY_ADD
STORE_FAST 1 (y)
LOAD_FAST 1 (y)
RETURN_VALUE
------------------ ------------------
def a2(x): LOAD_CONST 1 (2)
inc = 2 STORE_FAST 1 (inc)
y = x + inc LOAD_FAST 0 (x)
return y LOAD_FAST 1 (inc)
BINARY_ADD
STORE_FAST 2 (y)
LOAD_FAST 2 (y)
RETURN_VALUE
some = SomeObject()
# blah blah including last touch of `some`
# ...
pdb.set_trace()
# Look, Ma! I'm monkey-patching!
if some.some_property:
# oops, `some` was GC'd - DIE DIE DIE