You are doing multiple things incorrectly, so let’s see go through it:
In your code, predicate
is a generator for a decorator; the actual decorator is _predicate
. Decorators take a function and return a function; the latter will be assigned where the original code was added. So with your @predicate(…) def second_of_two
, the name second_of_two
will get the value that the decorator returns.
So let’s see what your decorator does:
def _predicate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
…
return wrapper
It builds a new internal function, uses functool.wraps
(which is good practice), and then returns that function. So far so good; so the second_of_two
gets the decorated function correctly.
Now, what does the wrapper function do though?
def wrapper(*args, **kwargs):
func.meta = {
'author': author,
'version': version
}
func.meta.update(others)
func(*args, **kwargs)
The wrapper function sets those meta data on the original function object. That’s the first mistake; you set the meta data on the original function which later isn’t referred to again. second_of_two
is the wrapper function, so it doesn’t have those meta functions. In addition, you only set those meta data when the function runs. So if you set the meta data correctly on the wrapper function, then it would still be only available once you have called it once. And the third mistake is that you call the original function and simply throw away its return value. That’s why you didn’t get any output.
So what should you do instead? First of all, set the meta data outside of the wrapper function, and set it on the function you return. And inside the wrapper function, return the original function’s return value:
def predicate(author, version, **others):
def _predicate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper.meta = {
'author': author,
'version': version
}
wrapper.meta.update(others)
return wrapper
return _predicate
And now, your wrapper
function no longer does anything special, so you can just use the original function instead:
def predicate(author, version, **others):
def _predicate(func):
func.meta = {
'author': author,
'version': version
}
func.meta.update(others)
return func
return _predicate