使用Pyinvoke @task装饰器的多个装饰器

时间:2016-04-05 22:06:59

标签: python-2.7 pyinvoke

我正在尝试创建一个装饰器,并将其与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之前还是之后,我都输了 调用任务功能,它无法识别函数名称..

我在这里做错了什么?

2 个答案:

答案 0 :(得分:2)

语法:

@deco1
@deco2
def func(): pass

几乎相当于:

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

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

您在库中使用的task装饰器返回一个Task对象而不是另一个函数,因此当使用包装器在全局命名空间中替换它时,您的外部装饰器可能无法做正确的事情功能。为了使其有效,您需要将wrapper作为一个模仿Task对象的所有相关行为的对象(我不知道可能有多么容易或困难)。< / p>

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

@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

答案 1 :(得分:0)

我无法获得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)

包装内部函数可防止其模糊基础函数,从而允许调用识别装饰器并检查其参数。