2

I followed this answer and implemented the following:

def B():
    try:
        raise Exception()
    except Exception as e:
        traceback_ob = e.__traceback__

        import traceback
        traceback_str = ''.join(traceback.format_exception(etype=type(e), value=e, tb=traceback_ob))

        print(traceback_str)

def A():
    B()

A()

The output was:

Traceback (most recent call last):
  File "/path/bespoke_traceback.py", line 3, in B
    raise Exception()
Exception

I need to have the full trace, so including A in the string - how can I achieve this?

To be specific - I need this in a string, not just printed.

Omroth
  • 883
  • 7
  • 24

3 Answers3

2

You can combine the format_exception function with the format_stack to get the previous frames:

import traceback

def B():
    try:
        raise Exception("error")
    except Exception as e:
        exception = traceback.format_exception(type(e), value=e, tb=e.__traceback__)
        stack = traceback.format_stack()

        # exception already holds the last (current) frame - we just want to add the previous frames
        exception[1:1] = stack[:-1]

        traceback_str = ''.join(exception)
        print(traceback_str)

def A():
    B()

A()

Will give:

Traceback (most recent call last):
  File "test.py", line 19, in <module>
    A()
  File "test.py", line 17, in A
    B()
  File "test.py", line 5, in B
    raise Exception("error")
Exception: error
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • 1
    Note that this won't work anymore, format_exception parameters changed in 3.10 – nurettin Jan 25 '23 at 12:51
  • @nurettin Thank you so much for letting me know. I changed the code and tested it and getting the same output from both Python 3.8 and 3.11. The change required was simply to make `type(e)` a positional argument as it was the only positional argument of the function (that didn't change) and only its name changed. So there was no reason to make it named from the first place... – Tomerikoo Jan 25 '23 at 13:07
0

My personal approach: (sorry for using a file but it seemed the only solution) Sorry if it's a bit complicated

from threading import Thread
import sys
import os

def B():
    raise Exception()
    
def A():
    B()

def catch_error(f):
    # redirect stderr (where errors are printed) to a temporary file
    # tried StringIO but it only showed part of the exception
    sys.stderr = open('temp', 'w')

    # run it in a thread so that the script doesn't stop after the error
    t = Thread(target=A) 
    t.start()
    t.join()

    # close the file and return stderr back to normal
    sys.stderr.close()
    sys.stderr = sys.__stderr__

    # get error string from the file and then delete it
    s = open('temp', 'r').read()
    os.remove('temp')

    # remove some lines concerning the threading module
    lines = s.split('\n')
    [lines.pop(2) for i in range(4)]
    lines.pop(0)

    # join the lines and return
    return '\n'.join(lines)

print(catch_error(A))

Output:

Traceback (most recent call last):
  File "C:\Users\lenovo\Amin\Workspace\test.py", line 9, in A
    B()
  File "C:\Users\lenovo\Amin\Workspace\test.py", line 6, in B
    raise Exception()
Exception
Amin Guermazi
  • 1,632
  • 9
  • 19
-1

You can achieve the traceback simply using the logging module no need for other modules:

For same file the exception traceback will be from immediate class. If it is call from different files the logger.exception will print entire traceback

check below example:

import logging
logger = logging.getLogger(__name__)
def B():
    try:
        raise Exception()
    except Exception as e:
        logger.exception("Error",stack_info=True)

def A():
    B()

A()
Amit Nanaware
  • 3,203
  • 1
  • 6
  • 19