1

I have a code snippet that I have wrapped in my own decorator. My decorator simply "catches" all exceptions, and then prints them, then re-raises them. However this is causing me an issue when I have multiple functions calling each other, as they're all getting printed. Example:

@error_wrapper
def myFunction(x, y):
    return int_div(x, y)
    
@error_wrapper
def int_div(x, y):
    return x//y

As you see from the code above, whenever y is 0, the function would error because of DivisionByZero. Now since both of these functions are wrapped in my error_wrapper, which is basically a decorator that does:

try:
    return func(*args, **kwargs)
except Exception as e:
    print(e)
    raise

The problem here is that the raise is happening in myFunction and in int_div, which is a side effect of wrapping both functions. So what solution I'm satisfied with is instead of raiseing the error again, I would just do a return with no return value, but this would make me lose the traceback, which is necessary for me to debug the code. I have seen the traceback module, and I've tried it, but now the problem I'm facing is the return is returning None, which is not the intended behavior. Any insight on how to tackle this?

3
  • just curious... why? uncaught exceptions get dumped to the command line anyway. Why not just catch the errors you might expect where you would expect them, and let the VM handle the rest Commented Aug 30, 2021 at 16:05
  • @DavidCulbreth Because this is going to end up in a GUI application, so eventually I would pop up a message box that contains the error details to the user, and it's currently popping up twice. Commented Aug 30, 2021 at 16:12
  • 2
    In that case, you should catch the error as far up the stack as you can go before needing to display it to the user. the answer I provided may function moderately, but I'm not happy about it. :P Commented Aug 30, 2021 at 16:19

1 Answer 1

3

You can simply set a flag on the error itself, and then check for said flag. I'm using something verbose to hopefully avoid any name collisions.

def error_wrapper(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            if not hasattr(e, "_error_wrapper_printed"):
                e._error_wrapper_printed = True
                print(e)
            raise
    return wrapper


@error_wrapper
def myFunction(x, y):
    return int_div(x, y)
    
@error_wrapper
def int_div(x, y):
    return x//y

print(myFunction(1, 0))

The above yields behavior which (I believe) fits what you need.

integer division or modulo by zero
Traceback (most recent call last):
  File "c:\Users\david\Desktop\tmp.py", line 21, in <module>
    print(myFunction(1, 0))
  File "c:\Users\david\Desktop\tmp.py", line 4, in wrapper
    return func(*args, **kwargs)
  File "c:\Users\david\Desktop\tmp.py", line 15, in myFunction
    return int_div(x, y)
  File "c:\Users\david\Desktop\tmp.py", line 4, in wrapper
    return func(*args, **kwargs)
  File "c:\Users\david\Desktop\tmp.py", line 19, in int_div
    return x//y
ZeroDivisionError: integer division or modulo by zero
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, I tried doing something similar but it didn't occur to me at all to add the flag into the exception itself! I was currently trying to create a new Exception class called DuckedException, but this flag solves everything!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.