python - "Online"函数的猴子修补

标签 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)
        import pdb; pdb.set_trace()
    #continue processing

这个方便的构造在遇到未知数据时会启动交互式调试器,因此我可以随意检查它并更改 process_a_lot 代码以正确处理它。

这里的问题是,当 data_stream 很大时,您真的不想再次咀嚼所有数据(假设 next 很慢,所以您无法保存您已有的内容并在下次运行时跳过)

当然你也可以在调试器中一次随意替换其他函数。您也可以替换函数本身,但它不会更改当前的执行上下文。

编辑: 由于有些人正在偏离轨道: 我知道有很多方法可以构建您的代码,使您的处理函数与 process_a_lot分开。我并没有真正询问构建代码的方法,而是询问如何从代码未准备好处理替换的情况中恢复(在运行时)。

最佳答案

首先是(原型(prototype))解决方案,然后是一些重要的注意事项。

# 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)

和:

# handlers.py

def handle_default(x):
    print "handle_default: {0!r}". format(x)

handler = {
    int: handle_default,
    str: handle_default
}

在 Python 2.7 中,这为您提供了一个字典,将预期/已知类型链接到处理每种类型的函数。如果没有可用于某个类型的处理程序,用户将自己放入调试器中,让他们有机会使用适当的处理程序修改 handlers.py 文件。在上面的示例中,没有用于 floatcomplex 值的处理程序。当它们出现时,用户将不得不添加适当的处理程序。例如,可以添加:

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)

好的,这样基本上就可以了。您可以将其改进和调整为更适合生产的形式,使其与 Python 2 和 3 等兼容。

在这样做之前,请深思熟虑。

这种“实时修改代码”的方法是一种极其脆弱且容易出错的方法。它鼓励您在紧要关头进行实时修复。这些修复可能没有经过良好或充分的测试。几乎根据定义,你刚刚发现你正在处理一个新类型 T。你还不太了解 T,它为什么会发生,它的边缘情况和失败模式可能是什么,等等。如果你的“修复”代码或热补丁不起作用,然后呢?当然,您可以加入更多的异常处理,捕获更多的异常类,并可能继续。

Web 框架,如 Flask具有基本上以这种方式工作的 Debug模式。但那些是 Debug模式,通常不适合生产。此外,如果您在调试器中输入了错误的命令怎么办?不小心输入了“quit”而不是“continue”,整个​​程序就结束了,随之而来的是你希望继续处理的愿望。如果这是用于调试(可能是探索新型数据流),请访问。

如果这是用于生产,请考虑使用 strategy that sets aside unhandled-types用于异步、带外检查和校正,而不是将开发人员/操作人员置于实时处理流程的中间。

关于python - "Online"函数的猴子修补,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26763993/

相关文章:

c++ - Python 和 float ——只打印整数

python - 通过 Python 中的 Git 钩子(Hook)更新 Git 存储库

Python - 'ascii' 编解码器无法解码字节

ruby - 猴子补丁类方​​法

python - 错误栏不在数据处

python - 从字符串序列创建 Python lambda 函数

Python 方法调用

c++ - 为什么函数名称被归类为 L 值表达式?

perl - 如何在Perl中制作 “final”方法?

perl - Perl 猴子修补是否允许您查看修补包的范围?