我正在尝试创建一个装饰器,并将其与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
之前还是之后,我都输了
调用任务功能,它无法识别函数名称..
我在这里做错了什么?
答案 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)
包装内部函数可防止其模糊基础函数,从而允许调用识别装饰器并检查其参数。