全局查找函数的导入

问题描述 投票:0回答:1

我正在为斜杠框架开发一个测试装置,它需要修改

time.sleep
的行为。由于原因我无法使用 pytest,所以我正在尝试推出自己的基本猴子补丁支持。

我能够轻松地替换

time.sleep
来替换
import time
的东西,但有些东西在我的装置实例化之前就可以
from time import sleep
。到目前为止,我正在使用
gc.get_referrers
来追踪对
sleep
的任何引用,然后再替换它们:

self._real_sleep = time.sleep
for ref in gc.get_referrers(time.sleep):
    if (isinstance(ref, dict)
            and "__name__" in ref
            and "sleep" in ref
            and ref["sleep"] is self._real_sleep):
        self._monkey_patch(ref)

实际上这可行,但感觉很丑陋。我确实可以访问相当当前的 python(当前为 3.11),但添加第 3 方依赖项的能力有限。是否有更好/更安全的方法来仅使用标准库方法来查找对某个事物的引用或在全局范围内修补某个事物?

python reflection
1个回答
0
投票

您可以使用Python的

sys.modules
来全局替换
time.sleep
。这包括直接导入
time.sleep
以及使用
from time import sleep
的情况。

这是代码。

import time
import sys
import gc

class TimeSleepPatch:
    def __init__(self, new_sleep_func):
        self._new_sleep_func = new_sleep_func
        self._real_sleep = time.sleep
        self._patched_modules = []

    def _patch_module(self, module, attr_name):
        # Replace the reference to the original time.sleep with the patched one
        if hasattr(module, attr_name):
            if getattr(module, attr_name) is self._real_sleep:
                setattr(module, attr_name, self._new_sleep_func)
                self._patched_modules.append((module, attr_name))

    def patch(self):
        # Patch the time module itself
        time.sleep = self._new_sleep_func

        # Go through all modules and replace any reference to time.sleep
        for module in sys.modules.values():
            if module:
                self._patch_module(module, "sleep")

        # Optional: Patch referrers in case any obscure reference still lingers
        for ref in gc.get_referrers(self._real_sleep):
            if isinstance(ref, dict):
                for key, value in ref.items():
                    if value is self._real_sleep:
                        ref[key] = self._new_sleep_func

    def restore(self):
        # Restore the original time.sleep
        time.sleep = self._real_sleep

        # Restore patched modules to their original state
        for module, attr_name in self._patched_modules:
            setattr(module, attr_name, self._real_sleep)
        self._patched_modules.clear()

# Example of a custom sleep function
def fake_sleep(duration):
    print(f"Fake sleep for {duration} seconds")

# Instantiate the patcher and patch the time.sleep
patcher = TimeSleepPatch(fake_sleep)
patcher.patch()

# Test it (this will print "Fake sleep for 2 seconds")
time.sleep(2)

# Restore the original time.sleep
patcher.restore()

# Test the restored behavior (this will actually sleep)
time.sleep(2)

希望这对你有一点帮助。

© www.soinside.com 2019 - 2024. All rights reserved.