使用Inspect模块执行特定cod

2024-05-15 18:07:16 发布

您现在位置:Python中文网/ 问答频道 /正文

为了简单起见,我把我的问题缩小到以下几点:

我有一个执行一系列函数的主函数。在第一个函数manipulate()中,我希望能够阻止test()使用sys.exit()退出程序。我还需要能够使print("Silence me")静音,这意味着这不应该出现在我的程序的输出中。最后,我的程序仍然需要能够从test()函数输出print("You need to hear this")

def main():
    manipulate()
    test()
def manipulate():

    print("Silence me")
    sys.exit()
    print("You need to hear this")

如果我只能更改operate()函数中的代码,如何实现这一点

我尝试过使用inspect模块,但是我想我可能遗漏了一些东西。我不确定解析test()的代码并通过exec()运行解析的代码是否是正确的方法

def manipulate():
    def filter_exc(func):
        src = inspect.getsource(func)

        lines = src.split('\n')
        out = lines[0] + "\n"

        for line in lines[1:]:
            m = re.match('(\s*)(.*)', line)
            lead, text = m.groups()
            if 'sys.exit()' in line:
                continue
            if 'Silence Me' in line:
                continue
            out += "    " + text + '\n'

        return out

    exec(filter_exc(game_on))

Tags: 函数代码intest程序defsysline
2条回答

在这个简单的例子中,我将只使用“monkeypatch”受影响的功能:

from contextlib import contextmanager
import sys

def test():
    print("Silence me")
    sys.exit()
    print("You need to hear this")

@contextmanager
def manipulate():
    global print

    try:
        _sys_exit_backup, sys.exit = sys.exit, lambda: None
        i = iter([lambda *args:None, print])
        _print_backup, print = print, lambda *args: next(i)(*args)
        yield
    finally:
        sys.exit = _sys_exit_backup
        print = _print_backup

def main():
    with manipulate():
        test()

main()

印刷品:

You need to hear this

首先,你要知道你做的是坏事。设计的编程语言执行源代码,而不是限制执行。编辑实时字节码是有害的,会产生无法修正的错误

撇开免责声明不谈,通过使用标准库中的patch来替换exitprint的标准实现,可以更干净地实现这种效果,但是您需要执行

from unittest.mock import patch
import sys


def my_print(*args, **kwargs):
    # do whatever
    return


def my_exit(*args, **kwargs):
    # do whatever
    return


def perform_patch():
    patchers = [
        patch('__main__.print', my_print),
        patch('sys.exit', my_exit)
    ]
    for patcher in patchers:
        patcher.start()
    return patchers


def perform_unpatch(patchers):
    for patcher in patchers:
        patcher.stop()


def manipulate():
    print("hello")
    sys.exit(1)


def main():
    patchers = perform_patch()
    manipulate() 
    perform_unpatch(patchers)
    print("I'm done!")


if __name__ == '__main__':
    main()

脚本只输出“我完成了!”

exitprint保持修补状态,直到调用patcher.stop()patch可以在with块中使用,因此如果可以将manipulate()调用放在with语句中,就可以更简洁地完成

另一种方法是获取源代码并使用ast模块动态重写它。这是一个talk about ASTs

最后你可以直接用字节码做一些事情,这里有一个talk exploring that

最后,我郑重地鼓励您使用任何其他可能的方式来编辑源代码。如果它是一个库,那么你最好直接用fork来编辑源代码

相关问题 更多 >