3

这个问题是对 Python 中装饰器的精彩回答 的后续:

我使用给定的“片段使任何装饰器通常接受任何参数”。

然后我有这个(这里是简化的)装饰器:

@decorator_with_args
def has_permission_from_kwarg(func, *args, **kwargs):
    """Decorator to check simple access/view rights by the kwarg."""
    def wrapper(*args_1, **kwargs_1):
        if 'kwarg' in kwargs_1:
            kwarg = kwargs_1['kwarg']
        else:
            raise HTTP403Error()

        return func(*args_1, **kwargs_1)

    return wrapper
  1. 与这个装饰器一起工作,没问题,它做得很好。
  2. 测试一个完全不需要 kwargs 的类似装饰器,结果相同。
  3. 但是用以下模拟测试这个装饰器不起作用:

    def test_can_access_kwarg(self):
        """Test simple permission decorator."""
        func = Mock(return_value='OK')
        decorated_func = has_permission_from_slug()(func(kwarg=self.kwarg))
        # It will raise at the following line, whereas the kwarg is provided...
        response = decorated_func()
        self.assertTrue(func.called)
        self.assertEqual(response, 'OK')
    

当我没有“kwarg”关键字参数时,它会返回我提出的异常......

有没有人知道如何测试(最好通过模拟)这样一个由另一个装饰器装饰的装饰器,它需要访问传递给函数的关键字参数之一?

4

1 回答 1

2
decorated_func = has_permission_from_slug()(func(kwarg=self.kwarg))

这会:

  1. 执行func(kwarg=self.kwarg)
  2. 生成实际装饰器的实例。
  3. 在函数调用的结果(即结果)上调用装饰器。
  4. 返回包装器,稍后将尝试调用步骤 3 的结果(这将失败)。

    响应 = 装饰函数()

这将调用不带参数的返回包装器,因此**kwargs_1为空。此外,如果您的包装器在这种情况下不会引发异常,则后续调用func(..)将引发异常,因为func(原始)的返回值可能不可调用(见上文)。

你可能想要做的,或者至少你的装饰器支持的,是这样的:

decorated_func = has_permission_from_kwarg()(func)
response = decorated_func(kwarg=self.kwarg)

或者,如果你想像这样传递你kwarg的装饰器:

decorated_func = has_permission_from_kwarg(kwarg=self.kwarg)(func)
response = decorated_func()

然后你需要调整或装饰器来实际使用kwargs,而不是kwargs_1在检查中(后者是装饰函数的参数)。


我正在decorator_with_args使用以下代码测试您的原始装饰器定义(没有更改)以及链接答案中定义的定义:

class HTTP403Error (Exception):
    pass

def func (*args, **kwargs):
    print('func {}; {}'.format(args, kwargs))

my_kwarg = 'foo'
decorated_func = has_permission_from_kwarg()(func)
decorated_func(kwarg=my_kwarg)
decorated_func(not_kwarg=my_kwarg)

正如预期的那样,我func (); {'kwarg': 'foo'}在第一次调用时打印出来,第二次调用时出现 HTTP403 异常。

于 2012-11-13T11:40:42.633 回答