在 Pyinvoke @task 装饰器中使用多个装饰器
Using multiple decorators with Pyinvoke @task decorator
我正在尝试创建装饰器,并将其与 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
之前还是之后,我都输了
调用任务功能,它无法识别函数名称..
我做错了什么?
语法:
@deco1
@deco2
def func(): pass
几乎等同于:
def func(): pass
func = deco1(deco2(func))
所以第一行的装饰器在第二行的装饰器之后应用。
您正在使用的 task
装饰器来自您的库 returns 一个 Task
对象而不是另一个函数,因此您的外部装饰器在替换它时可能没有做正确的事情具有包装函数的全局命名空间。为了让它工作,你需要你的 wrapper
成为一个模仿 Task
对象的所有相关行为的对象(我不知道这可能有多容易或多难)。
更直接的方法可能是颠倒装饰器的顺序:
@task
@extract_config
def zip_files(config):
这可能更接近工作,但我怀疑它仍然出错,原因很简单。您的 extract_config
装饰器 returns 一个与 zip_files
名称不同的函数(它 returns 名为 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
我无法使用 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)
包装内部函数可以防止它掩盖底层函数,允许调用识别装饰器并允许它检查函数的参数。
我正在尝试创建装饰器,并将其与 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
之前还是之后,我都输了
调用任务功能,它无法识别函数名称..
我做错了什么?
语法:
@deco1
@deco2
def func(): pass
几乎等同于:
def func(): pass
func = deco1(deco2(func))
所以第一行的装饰器在第二行的装饰器之后应用。
您正在使用的 task
装饰器来自您的库 returns 一个 Task
对象而不是另一个函数,因此您的外部装饰器在替换它时可能没有做正确的事情具有包装函数的全局命名空间。为了让它工作,你需要你的 wrapper
成为一个模仿 Task
对象的所有相关行为的对象(我不知道这可能有多容易或多难)。
更直接的方法可能是颠倒装饰器的顺序:
@task
@extract_config
def zip_files(config):
这可能更接近工作,但我怀疑它仍然出错,原因很简单。您的 extract_config
装饰器 returns 一个与 zip_files
名称不同的函数(它 returns 名为 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
我无法使用 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)
包装内部函数可以防止它掩盖底层函数,允许调用识别装饰器并允许它检查函数的参数。