使用Inspect模块执行特定代码

时间:2019-06-29 20:16:16

标签: python bytecode

为简单起见,我将问题缩小到以下范围:

我有一个执行一系列功能的主要功能。从第一个功能manipulate()开始,我希望能够防止test()sys.exit()退出程序。我还需要能够使print("Silence me")保持沉默,这意味着它不应显示在程序的输出中。最后,我的程序仍然需要能够从print("You need to hear this")函数中输出test()

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

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

如果我只能在handler()函数中更改代码,该如何实现?

我已经尝试研究使用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))

2 个答案:

答案 0 :(得分:0)

在这个简单的示例中,我只是“ 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

答案 1 :(得分:0)

首先,您需要知道自己在做什么是不好的。设计的编程语言会执行源代码,而不是限制执行。编辑实时字节码是邪恶的,并且会产生不可辩驳的错误。

抛弃免责声明,您可以通过使用标准库中的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

最后,我强烈建议您使用任何其他可能的方式来编辑源。如果它是一个库,那么如果您将它派生并直接编辑源代码,那就更好了。