Closed tlambert03 closed 4 years ago
Totally untested, but maybe something like:
import inspect
import wrapt
import requests
def argspec_factory(wrapped):
args, *rest, annotations = inspect.getfullargspec(wrapped)
# change annotation of first argument
annotations[args[0]] = requests.Response
argspec = inspect.FullArgSpec(args, *rest, annotations)
ns = {}
exec('def adapter{}: pass'.format(inspect.formatargspec(*argspec)), ns, {'requests': requests})
return ns['adapter']
@wrapt.decorator(adapter=wrapt.adapter_factory(argspec_factory))
def my_adapter(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
@my_adapter
def test(arg):
pass
In other words, you do the work to construct the fake function object with the required prototype.
Thanks! Great to have an example. That should get me started. I was also coming to the realization that I could perhaps use typing.ForwardRef
, and deal with it on the introspection side?
I needed to make some slight modifications to this example, since 'adapter'
was not found in ns
unless I changed the call to exec. Here's what I ended up with in case anyone stumbles across this.
(I also use Signature Objects instead of ArgSpec tuples)
def argspec_factory(wrapped):
sig = inspect.signature(wrapped)
p1, *rest = sig.parameters.values()
p1 = p1.replace(annotation=requests.Response)
new_sig = sig.replace(parameters=[p1, *rest])
ns = {"requests": requests}
exec(f"def adapter{new_sig}: pass", ns, ns)
return ns["adapter"]
thanks for the help @GrahamDumpleton
I'm trying to create an adaptor decorator that will add/modify the annotation of an argument following the general pattern in the docs. However, if I try to change the annotation to anything but a builtin class, I get a
NameError
saying the module is not defined. Here's a toy example usingrequests.Response
:raises:
NameError: name 'requests' is not defined
Traceback
``` Traceback (most recent call last): File "examples/test.py", line 18, inlooking through the source code, it seems as if this is by design, as an empty namespace is handed to the
exec_
function ... presumably for security reasons. https://github.com/GrahamDumpleton/wrapt/blob/0c98567e78837bb39b2a498c13a0eb1403266bee/src/wrapt/decorators.py#L206-L209do you have a suggestion for how I could accomplish what I'm trying to do? in the example above, I would like to have the wrapped function show this signature:
thanks!