-1

I have a decorator that I use to suppress and log exceptions within a function. The code is something like this:

def log_exceptions(func):
    def wrapper(*args, **kwargs):
        try:
            ret = func(*args, **kwargs)
        except Exception as e:
            print(e)
            print(args, kwargs)  # also log what arguments caused the exception
        return ret
    return wrapper

A problem here is that, it is hard to manually match the printed argument values to the argument names of the function, because positional arguments could also go inside kwargs, and there may be args and kwargs arguments in the inner function as well. So, it would be useful to match the args and kwargs values in the wrapper with argument names in the inner function.

My question is then, is there a built-in way to do this matching? If not, what would be an elegant way to implement it?

martineau
  • 119,623
  • 25
  • 170
  • 301
Zecong Hu
  • 2,584
  • 18
  • 33
  • Also see [this one](https://stackoverflow.com/questions/582056/getting-list-of-parameter-names-inside-python-function) – Pynchia Jan 20 '20 at 23:07
  • 1
    @Pynchia: That's part of the solution, but it doesn't show how to perform the matching. (Admittedly, it is the hard part - implementing `bind` is more straightforward and requires less knowledge than implementing `signature` - but reimplementing `bind` would still be a lot of pointless work.) – user2357112 Jan 20 '20 at 23:24

1 Answers1

1

If the signature can be determined, then inspect.signature(func).bind(*args, **kwargs) does the job.

inspect.signature(func) (lowercase s) attempts to compute a Signature object (capital S) representing a callable's signature, and the resulting Signature's bind method matches the signature against provided arguments, returning a BoundArguments object. The arguments attribute of the resulting object is an ordered mapping of parameter names to values, excluding arguments that fell back on default values.

Here's an example of how you might use it:

import inspect

try:
    sig = inspect.signature(func)
except (TypeError, ValueError):
    print('Could not determine function signature.')
else:
    try:
        bound = sig.bind(*args, **kwargs)
    except TypeError:
        print('Could not bind arguments to function signature.')
        print('Signature:', sig)
        print('Positional arguments:', args)
        print('Keyword arguments:', kwargs)
    else:
        # You can use bound.apply_defaults() to include defaults
        print('Bound arguments (defaults not included):')
        for name, value in bound.parameters:
            print('{}: {!r}'.format(name, value))
user2357112
  • 260,549
  • 28
  • 431
  • 505