I am attempting to create a decorator, and use it alongside the pyinvoke @task
decorator.
See:
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
...
However, now from the command line when I execute
inv zip_files
I receive the output:
<Task 'zip_files'>
No idea what 'zip_files' is!
No matter if @task
comes before or after @extract_config
, I lose
the invoke task functionality and it doesn't recognize the function name..
What am I doing wrong here?
The syntax:
@deco1
@deco2
def func(): pass
Is pretty much equivalent to:
def func(): pass
func = deco1(deco2(func))
So the decorator on the first line is applied after the decorator on the second line.
The task
decorator you are using from your library returns a Task
object rather than another function, so your outer decorator may not be doing the right thing when replacing it in the global namespace with a wrapper function. To make it work, you'd need your wrapper
to be an object that mimics all the relevant behavior of the Task
object (I have no idea how easy or difficult that might be).
A more straight forward approach may be to reverse the order of the decorators:
@task
@extract_config
def zip_files(config):
This is probably closer to working, but I suspect it still goes wrong for a simple reason. Your extract_config
decorator returns a function with a different name than zip_files
(it returns the function named wrapper
, which hasn't made any effort to change its __name__
attribute), so the Task
object doesn't know its name properly.
To fix this, I'd suggest using functools.wraps
in the decorator to copy the relevant attributes of the wrapped function into the wrapper function:
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