假设我们有两个文件:
to_patch.py
from unittest.mock import patch
def patch_a_function():
print("Patching!")
patcher = patch("to_be_patched.function")
patcher.start()
print("Done patching!")
to_be_patched.py
from to_patch import patch_a_function
def function():
pass
patch_a_function()
function()
然后我们运行python -m to_be_patched
。这将输出:
Patching!
Patching!
- 为什么没有打印
Done patching!
? - 为什么
Patching!
打印两次?
我已将答案缩小到 (2);对 patch.start
的调用似乎再次触发 patch_a_function
。我怀疑这是因为它是在 to_be_patched.py
中导入的,但我不确定为什么该函数本身会第二次运行。同样,我不确定为什么在对 patch_a_function
的任何调用中都没有到达 Done patching!
行。 patcher.start()
不能阻塞,因为程序很好地退出而不是卡在那里......对吗?
编辑:呵呵。看起来没有人可以重现完成修补!
没有被打印(这确实是主要困难) - 所以我想这只是我这边的问题
最佳答案
- Why isn't
Done patching!
ever printed?
无法重现。
$ python -m to_be_patched
Patching!
Patching!
Done patching!
Done patching!
- Why is
Patching!
printed twice?
您的模块被导入两次。如果将 print(__name__)
添加到文件 to_be_patched.py
中,就会很清楚:
from to_patch import patch_a_function
print(f"{__name__=}")
def function():
pass
patch_a_function()
function() # note: this line doesn't actually do anything, and could be commented out
结果:
$ python -m to_be_patched
__name__='__main__'
Patching!
__name__='to_be_patched'
Patching!
Done patching!
Done patching!
当您使用python -m to_be_patched
时,您的模块to_be_patched
将被加载为top-level code ,即模块 __name__
将是 "__main__"
。
当使用mock.patch
时,mock将first import the patch target 。当以像 "to_be_patched.function"
这样的字符串形式给出补丁目标时,模拟将使用 importlib
,通过 pkgutil.resolve_name
,找到要修补的正确命名空间。该方法加载__name__
为"to_be_patched"
的目标模块,它不是顶级代码环境。尽管加载的是相同的底层 .py 文件,但由于名称不匹配,sys.modules
中存在缓存未命中:"__main__"!= "to_be_patched"
。
函数patch_a_function
现在具有双重身份,并且存在于模块__main__
以及模块to_be_patched
中,所以你所看到的每个人都被叫到。通过所描述的双重导入机制,第一个调用触发第二个调用。
关于python - 为什么unittest的 `mock.patch.start`会重新运行启动补丁程序的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77371281/