Jonas Sorgenfrei
Jonas Sorgenfrei

Reputation: 43

Python Raising Exception with filtered traceback

I'm trying to raise an exception with a stacktrace that doesn't include the last Frame.

In python 2 it was possible to remove the last entry from the strack like this:

def exceptionCatcher(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception:
            excInfo = sys.exc_info()
            # some custom logging stuff
            raise excInfo[0], excInfo[1], excInfo[2].tb_next
    return wrapper

def a():
    raise Exception("My Exception")

@exceptionCatcher
def test():
    a()

This worked in python and excluded the last exceptionCatcher call itself from the traceback e.g.

Traceback (most recent call last):
  File "test.py", line 15, in <module>
    test()
  File "test.py", line 12, in a
    raise Exception("a")
Exception: a 

instead of

Traceback (most recent call last):
  File "test.py", line 12, in <module>
    test()
  File "test.py", line 9, in wrapper
    raise e
  File "test.py", line 5, in wrapper
    return func(*args, **kwargs)
  File "test.py", line 10, in test
    a()
  File "test.py", line 5, in a
    raise Exception("a")
Exception: a

Switching to python3 it's not possible to raise an exception like: raise excInfo[0], excInfo[1], excInfo[2].tb_next

and i had to use:

def exceptionCatcher(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            excInfo = sys.exc_info()
            # some custom logging stuff
            raise e.with_traceback(excInfo[2].tb_next)
...  

Which added the following line to my traceback:

Traceback (most recent call last):
  File "test.py", line 12, in <module>
    test()
  File "test.py", line 9, in wrapper
    raise e.with_traceback(excInfo[2].tb_next)
  File "test.py", line 10, in test
    a()
  File "test.py", line 5, in a
    raise Exception("a")
Exception: a

Is there any way to get rid of this part of the traceback?

I know exceptionCatching can be done with the exceptionhook but in the scenario i'm using this it's not possible to use the exceptionhook as it's another third party application (in cpp) which seems to catch the exception so the exceptionhook won't catch any. So i came up with the idea of using a wrapper decorator as shown above eventho it's a hacky way.

Upvotes: 1

Views: 75

Answers (1)

chepner
chepner

Reputation: 531085

Instead of a decorator, use a context manager, which doesn't add a stack frame in the first place.

import contextlib


@contextlib.contextmanager
def exceptionCatcher():
    try:
        yield
    except Exception as e:
        print("custom logging")
        raise


def a():
    raise Exception("My Exception")


def test():
    with exceptionCatcher():
        a()


test()

Upvotes: 1

Related Questions