【问题标题】:Using multiple decorators with Pyinvoke @task decorator在 Pyinvoke @task 装饰器中使用多个装饰器
【发布时间】:2016-07-26 01:50:46
【问题描述】:

我正在尝试创建一个装饰器,并将其与 pyinvoke @task 装饰器一起使用。

见:

def extract_config(func):
    print func

    def func_wrapper(cfg=None):
        config = read_invoke_config(cfg)
        return func(**config)

    return func_wrapper


@extract_config
@task
def zip_files(config):
    import zipfile
    ...

但是,现在我从命令行执行

inv zip_files

我收到输出:

<Task 'zip_files'> No idea what 'zip_files' is!

不管@task@extract_config之前还是之后,我都输了 调用任务功能,它无法识别函数名称..

我在这里做错了什么?

【问题讨论】:

标签: python-2.7 pyinvoke


【解决方案1】:

语法:

@deco1
@deco2
def func(): pass

几乎等同于:

def func(): pass
func = deco1(deco2(func))

所以第一行的装饰器应用在第二行的装饰器之后。

您从库中使用的task 装饰器返回一个Task 对象而不是另一个函数,因此当您在全局命名空间中用包装函数替换它时,您的外部装饰器可能没有做正确的事情。为了让它工作,你需要你的 wrapper 是一个模仿 Task 对象的所有相关行为的对象(我不知道这可能是容易还是困难)。

更直接的方法可能是颠倒装饰器的顺序:

@task
@extract_config
def zip_files(config):

这可能更接近工作,但我怀疑它仍然出错,原因很简单。您的extract_config 装饰器返回一个名称与zip_files 不同的函数(它返回名为wrapper 的函数,它没有努力更改其__name__ 属性),所以Task 对象不会不知道它的名字。

为了解决这个问题,我建议在装饰器中使用functools.wraps 将包装函数的相关属性复制到包装函数中:

def extract_config(func):
    print func

    @functools.wraps(func)
    def func_wrapper(cfg=None):
        config = read_invoke_config(cfg)
        return func(**config)

    return func_wrapper

【讨论】:

    【解决方案2】:

    我无法让 Blcknght 的解决方案发挥作用,但我找到了解决方法:

    def decorator(fn):
        @functools.wraps(fn)
        def _decorated(ctx, *args, **kwargs):
            return fn(ctx, *args, **kwargs)
        return _decorated
    
    @invoke.task
    def some_task(ctx, config="Something"):
        @decorator
        def _inner(ctx):
            print(config)
            ...
    
        return _inner(ctx)
    

    包装内部函数可以防止它掩盖底层函数,允许调用识别装饰器并允许它检查函数的参数。

    【讨论】:

    • 感谢您的解决方案!在此示例中,仅添加 @functools.wraps(fn) 装饰器对我自己的装饰器起作用!
    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    • 2020-02-29
    • 2013-06-15
    • 2013-05-07
    • 2015-02-01
    • 2015-11-18
    相关资源
    最近更新 更多