Accessing Wrapped Function Parameters In An Easy Way
import functools
def decorator(func):
@functools.wraps(func)
def wrap(*args, **kwargs):
print(args, kwargs)
return func(*args, **kwargs)
return wrap
@decorator
def boo(a, b, c):
pass
boo(1, b=2, c=3)((1,), {'c': 3, 'b': 2})
The above codes is a general decorator function we always writen. The wrap inside the decorator uses (*args, **kwargs) as parameters for full compatibility with the wrapped func if we don’t know what parameters will be received.
import sys
import functools
import traceback
def decorator(func):
@functools.wraps(func)
def wrap(*args, **kwargs):
print(args, kwargs)
return func(*args, **kwargs)
return wrap
@decorator
def boo(a, b, c):
pass
try:
boo(1, b=2, c=3, d=4)
except TypeError:
traceback.print_exc(file=sys.stdout)((1,), {'c': 3, 'b': 2, 'd': 4})
Traceback (most recent call last):
File "<stdin>", line 19, in <module>
File "<stdin>", line 10, in wrap
TypeError: boo() got an unexpected keyword argument 'd'
You guys feel weird to see me using the
traceback.print_excandsys.stdout. I use the Org-Mode to write this blog. It has so many useful features. I use the code block syntax which can be evaluated the codes to get their output. Due to it only collects output that came from stdout, I need to redirect it from stderr into stdout.
We can see the boo does not validate the parameters before the actual boo function being invoked. So we may need to validate the parameters before the func being invoked. And how to accessing the right parameter a of function boo in wrap function if we needs to do something with it.
To achieve that we need to inspect the parameter signatures of the wrapped function.
Use inspect.signature1 to do that.
import sys
import inspect
import traceback
def boo(a, b, c):
pass
sig = inspect.signature(boo)
print(sig)
# accessing the specific parameter
bound_arguments = sig.bind(1, b=2, c=3)
print(bound_arguments)
print(bound_arguments.arguments['a'])
try:
# validate the arguments
sig.bind(1, b=2, c=3, d=4)
except TypeError:
traceback.print_exc(file=sys.stdout)(a, b, c)
<BoundArguments (a=1, b=2, c=3)>
1
Traceback (most recent call last):
File "<stdin>", line 18, in <module>
File "/usr/local/Cellar/python@3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 3062, in bind
return self._bind(args, kwargs)
File "/usr/local/Cellar/python@3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 3051, in _bind
raise TypeError(
TypeError: got an unexpected keyword argument 'd'
We see the Signature.bind method can be used easily to validate and to access the specific parameter.
Now we combine them together.
import sys
import inspect
import functools
import traceback
def decorator(func):
sig = inspect.signature(func)
@functools.wraps(func)
def wrap(*args, **kwargs):
bound_arguments = sig.bind(*args, **kwargs)
bound_arguments.apply_defaults()
print(bound_arguments)
return func(*args, **kwargs)
return wrap
@decorator
def boo(a, b, c):
pass
boo(1, b=2, c=3)
try:
boo(1, b=2, c=3, d=4)
except TypeError:
traceback.print_exc(file=sys.stdout)<BoundArguments (a=1, b=2, c=3)>
Traceback (most recent call last):
File "<stdin>", line 27, in <module>
File "<stdin>", line 12, in wrap
File "/usr/local/Cellar/python@3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 3062, in bind
return self._bind(args, kwargs)
File "/usr/local/Cellar/python@3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/inspect.py", line 3051, in _bind
raise TypeError(
TypeError: got an unexpected keyword argument 'd'
This useful function inspect.signature1 came from PEP 362. It returns a inspect.Signature2 object which provides very useful methods to access the right parameters or to do some other things. See the PEP for more additional information.
This blog is written via Org-Mode, then being converted into Markdown, being released via Hugo at last.
Footnotes
1 The document link is https://docs.python.org/3.6/library/inspect.html?highlight=inspect#inspect.signature
2 The document link is https://docs.python.org/3.6/library/inspect.html?highlight=inspect#inspect.Signature