sunyata
sunyata

Reputation: 2261

How to discard superfluous parameters when calling a function?

Background

I'm working on a PyQt application and have added the following debug decorator:

def debug_decorator(func):
    @functools.wraps(func)
    def wrapper_func(*args, **kwargs):
        logging.debug(f"Calling {func.__name__}")
        ret_val = func(*args, **kwargs)
        logging.debug(f"{func.__name__} returned {ret_val!r}")
        return ret_val
    return wrapper_func

Among other things, i use this for click handler functions, for example:

self.share_qpb = QtWidgets.QPushButton("Share")
self.share_qpb.clicked.connect(self.on_share_clicked)
[...]
@debug_decorator
def on_share_clicked(self):

If i run this code i get the following error when the share button is clicked:

TypeError: on_share_clicked() takes 1 positional argument but 2 were given

The reason for this is that the clicked signal in Qt/PyQt sends the checked state of the button (documentation)

This is only an issue when i decorate functions with my debug decorator, not otherwise (i guess the extra argument is discarded somewhere)

Question

How can i make my debug decorator work without having to add a parameter for every clicked signal handler?

(I'd like to find a solution which works both for cases when the arguments match and when there is one extra argument)


I'm running Python 3.8 and PyQt 5.15

Upvotes: 0

Views: 365

Answers (1)

sunyata
sunyata

Reputation: 2261

I managed to solve this by checking to see if the number of arguments provided len(args) is more than the number of arguments that the decorated function accepts func.__code__.co_argcount

I also check if the name of the decorated function contains the string "clicked" since i have made a habit of having that as part of the name of my click handler functions. This has the advantage that if another decorated function is called with too many arguments we will (as usual) get an error

def debug_decorator(func):
    @functools.wraps(func)
    def wrapper_func(*args, **kwargs):
        logging.debug(f"=== {func.__name__} was called")

        func_arg_count_int = func.__code__.co_argcount
        func_name_str = func.__name__

        if len(args) > func_arg_count_int and "clicked" in func_name_str:  # <----
            args = args[:-1]

        ret_val = func(*args, **kwargs)
        logging.debug(f"=== {func.__name__} returned {ret_val!r}")
        return ret_val
    return wrapper_func

Thank you to @ekhumoro for helping me out with this!

Upvotes: 1

Related Questions