在python中进行测试时,如何删除装饰器的效果? [英] How to remove the effects of a decorator while testing in python?
问题描述
我在python中的某些代码中使用retry
装饰器.但是我想通过消除其影响来加快测试速度.
I'm using the retry
decorator in some code in python. But I want to speed up my tests by removing its effect.
我的代码是:
@retry(subprocess.CalledProcessError, tries=5, delay=1, backoff=2, logger=logger)
def _sftp_command_with_retries(command, pem_path, user_at_host):
# connect to sftp, blah blah blah
pass
在测试时如何去除装饰器的效果?我无法创建未修饰的版本,因为我正在测试使用该版本的高级功能.
How can I remove the effect of the decorator while testing? I can't create an undecorated version because I'm testing higher-level functions that use this.
由于retry
使用time.sleep
退出,理想情况下,我可以修补time.sleep
,但是由于这是在装饰器中,所以我认为这是不可能的.
Since retry
uses time.sleep
to back off, ideally I'd be able to patch time.sleep
but since this is in a decorator I don't think that's possible.
有什么办法可以加快使用此功能的代码的测试速度?
Is there any way I can speed up testing code that uses this function?
更新
我基本上是在尝试测试使用此功能的更高级别的函数,以确保它们捕获到_sftp_command_with_retries
引发的所有异常.由于retry
装饰器将传播它们,因此我需要一个更复杂的模拟.
I'm basically trying to test my higher-level functions that use this to make sure that they catch any exceptions thrown by _sftp_command_with_retries
. Since the retry
decorator will propagate them I need a more complicated mock.
因此来自此处我可以看到如何模拟装饰器.但是现在我需要知道如何编写一个本身就是装饰器的模拟程序.它需要调用_sftp_command_with_retries
,如果它引发了异常,请传播该异常,否则返回返回值.
So from here I can see how to mock a decorator. But now I need to know how to write a mock that is itself a decorator. It needs to call _sftp_command_with_retries
and if it raises an exception, propagate it, otherwise return the return value.
在导入我的功能后添加此功能无效:
Adding this after importing my function didn't work:
_sftp_command_with_retries = _sftp_command_with_retries.__wrapped__
推荐答案
您正在使用的 retry
装饰器构建在 decorator.decorator
实用程序装饰器的顶部,并且如果未安装该软件包,则回退更为简单
The retry
decorator you are using is built on top of the decorator.decorator
utility decorator with a simpler fallback if that package is not installed.
结果具有__wrapped__
属性,可让您访问原始功能:
The result has a __wrapped__
attribute that gives you access to the original function:
orig = _sftp_command_with_retries.__wrapped__
如果未安装decorator
,并且您使用的是3.2之前的Python版本,则该属性将不存在.您将需要手动进入装饰器的关闭位置:
If decorator
is not installed and you are using a Python version before 3.2, that attribute won't be present; you'd have to manually reach into the decorator closure:
orig = _sftp_command_with_retries.__closure__[1].cell_contents
(索引0处的闭包是调用retry()
本身时产生的retry_decorator
).
(the closure at index 0 is the retry_decorator
produced when calling retry()
itself).
请注意,decorator
在retry
软件包元数据中被列为依赖项,并且如果您将其与pip
一起安装,则会自动安装decorator
软件包.
Note that decorator
is listed as a dependency in the retry
package metadata, and if you installed it with pip
the decorator
package would have been installed automatically.
您可以使用try...except
支持这两种可能性:
You can support both possibilities with a try...except
:
try:
orig = _sftp_command_with_retries.__wrapped__
except AttributeError:
# decorator.decorator not available and not Python 3.2 or newer.
orig = _sftp_command_with_retries.__closure__[1].cell_contents
请注意,您总是可以使用模拟补丁对time.sleep()
进行修补.装饰器代码将在引用time模块时使用该模拟程序>模块源代码.
Note that you always can patch time.sleep()
with a mock. The decorator code will use the mock as it references the 'global' time
module in the module source code.
或者,您可以使用以下方法修补retry.api.__retry_internal
:
Alternatively, you could patch retry.api.__retry_internal
with:
import retry.api
def dontretry(f, *args, **kw):
return f()
with mock.patch.object(retry.api, '__retry_internal', dontretry):
# use your decorated method
这会将执行实际重试的功能临时替换为直接调用原始功能的功能.
This temporarily replaces the function that does the actual retrying with one that just calls your original function directly.
这篇关于在python中进行测试时,如何删除装饰器的效果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!