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_exc
andsys.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.signature
1 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.signature
1 came from PEP 362. It returns a inspect.Signature
2 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