How can I wrap a python function in a way that works with with inspect.signature?

2024/10/15 4:26:03

Some uncontroversial background experimentation up front:

import inspectdef func(foo, bar):passprint(inspect.signature(func))  # Prints "(foo, bar)" like you'd expectdef decorator(fn):def _wrapper(baz, *args, *kwargs):fn(*args, **kwargs)return _wrapperwrapped = decorator(func)
print(inspect.signature(wrapped))  # Prints "(baz, *args, **kwargs)" which is totally understandable

The Question

How can implement my decorator so that print(inspect.signature(wrapped)) spits out "(baz, foo, bar)"? Can I build _wrapper dynamically somehow by adding the arguments of whatever fn is passed in, then gluing baz on to the list?

The answer is NOT

def decorator(fn):@functools.wraps(fn)def _wrapper(baz, *args, *kwargs):fn(*args, **kwargs)return _wrapper

That give "(foo, bar)" again - which is totally wrong. Calling wrapped(foo=1, bar=2) is a type error - "Missing 1 required positional argument: 'baz'"

I don't think it's necessary to be this pedantic, but

def decorator(fn):def _wrapper(baz, foo, bar):fn(foo=foo, bar=bar)return _wrapper

Is also not the answer I'm looking for - I'd like the decorator to work for all functions.

Answer

You can use __signature__ (PEP) attribute to modify returned signature of wrapped object. For example:

import inspectdef func(foo, bar):passdef decorator(fn):def _wrapper(baz, *args, **kwargs):fn(*args, **kwargs)f = inspect.getfullargspec(fn)fn_params = []if f.args:for a in f.args:fn_params.append(inspect.Parameter(a, inspect.Parameter.POSITIONAL_OR_KEYWORD))if f.varargs:fn_params.append(inspect.Parameter(f.varargs, inspect.Parameter.VAR_POSITIONAL))if f.varkw:fn_params.append(inspect.Parameter(f.varkw, inspect.Parameter.VAR_KEYWORD))_wrapper.__signature__ = inspect.Signature([inspect.Parameter("baz", inspect.Parameter.POSITIONAL_OR_KEYWORD),*fn_params,])return _wrapperwrapped = decorator(func)
print(inspect.signature(wrapped))

Prints:

(baz, foo, bar)

If the func is:

def func(foo, bar, *xxx, **yyy):pass

Then print(inspect.signature(wrapped)) prints:

(baz, foo, bar, *xxx, **yyy)
https://en.xdnf.cn/q/69329.html

Related Q&A

Python OpenCV Error: TypeError: Image data cannot be converted to float

So I am trying to create a Python Program to detect similar details in two images using Pythons OpenCV. I have the two images and they are in my current directory, and they exist (see the code in line…

Specify timestamp on each packet in Scapy?

With Scapy, when I create a packet and write it to a pcap file, it sets the timestamp of the packet to the current time.This is my current usage. 1335494712.991895 being the time I created the packet:&…

Converting a dataframe to dictionary with multiple values

I have a dataframe likeSr.No ID A B C D1 Tom Earth English BMW2 Tom Mars Spanish BMW Green 3 Michael Mercury Hindi …

How do I create KeyPoints to compute SIFT?

I am using OpenCV-Python.I have identified corner points using cv2.cornerHarris. The output is of type dst.I need to compute SIFT features of the corner points. The input to sift.compute() has to be of…

Error in Tensorboards(PyTorch) add_graph

Im following this Pytorchs Tensorboard documentation. I have the following code: model = torchvision.models.resnet50(False) writer.add_graph(model)It throws the following error:_ = model(*args) # dont…

Population must be a sequence or set. For dicts, use list(d)

I try to excute this code and I get the error bellow, I get the error in the random function and I dont know how to fix it, please help me.def load_data(sample_split=0.3, usage=Training, to_cat=True, v…

Why do imports fail in setuptools entry_point scripts, but not in python interpreter?

I have the following project structure:project |-project.py |-__init__.py |-setup.py |-lib|-__init__.py|-project|-__init__.py|-tools.pywith project.py:from project.lib import *def main():print("ma…

msgpack unserialising dict key strings to bytes

I am having issues with msgpack in python. It seems that when serialising a dict, if the keys are strings str, they are not unserialised properly and causing KeyError exceptions to be raised.Example:&g…

Better solution for Python Threading.Event semi-busy waiting

Im using pretty standard Threading.Event: Main thread gets to a point where its in a loop that runs:event.wait(60)The other blocks on a request until a reply is available and then initiates a:event.set…

\ufeff Invalid character in identifier

I have the following code :import urllib.requesttry:url = "https://www.google.com/search?q=test"headers = {}usag = Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefo…