Search code examples
pythondecorator

Class method decorator with arguments


So, here's a method:

def evaluate(self): return self.left ** self.right

And it can raise an OverflowError if the result is too big.
To fix it (or convert to a different exception), you can e.g. decorate it:

def overflow_adapter(method):
    def wrapper(self):
        try:
            return method(self)
        except OverflowError:
            raise ValueError("Result is too big!")
    return wrapper


@overflow_adapter
def evaluate(self): return self.left ** self.right

What I want to achieve is to be able to decorate methods with a specific decorator, which converts multiple different exceptions to one specific:

@exception_adapter(aware=[OverflowError,], response=ValueError("Result is too big!"))
def evaluate(self): return self.left ** self.right

I would write something like this (... are places I don't know how to complete:

def exception_adapter(...):
    def wrapper(...):
        try:
            return method(self)
        except BaseException as e:
            if e in aware: raise response
    return wrapper

How can I make this work?


Solution

  • Something like this:

    from functools import wraps
    
    
    def exception_adapter(
        aware: tuple[type[BaseException]],
        response: BaseException,
    ):
        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                try:
                    return func(*args, **kwargs)
                except aware as e:
                    raise response from e
    
            return wrapper
    
        return decorator
    
    
    @exception_adapter(
        aware=(OverflowError,),
        response=ValueError("Result is too big!"),
    )
    def evaluate(a, b):
        if a > 3:
            raise OverflowError("foo")
        return a ** b
    
    
    print(evaluate(2, 7))
    print(evaluate(5, 7))
    

    The output is

    Traceback (most recent call last):
      File "scratch_625.py", line 9, in wrapper
        return func(*args, **kwargs)
      File "scratch_625.py", line 21, in evaluate
        raise OverflowError("foo")
    OverflowError: foo
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "scratch_625.py", line 26, in <module>
        print(evaluate(5, 7))
      File "scratch_625.py", line 11, in wrapper
        raise response from e
    ValueError: Result is too big!