matt wilkie
matt wilkie

Reputation: 18084

Hide traceback unless a debug flag is set

What is the idiomatic python way to hide traceback errors unless a verbose or debug flag is set?

Example code:

their_md5 = 'c38f03d2b7160f891fc36ec776ca4685'
my_md5 = 'c64e53bbb108a1c65e31eb4d1bb8e3b7' 
if their_md5 != my_md5:
    raise ValueError('md5 sum does not match!')

Existing output now, but only desired when called with foo.py --debug:

Traceback (most recent call last):
  File "b:\code\apt\apt.py", line 1647, in <module>
    __main__.__dict__[command] (packages)
  File "b:\code\apt\apt.py", line 399, in md5
    raise ValueError('md5 sum does not match!')
ValueError: md5 sum does not match!

Desired normal output:

ValueError: md5 sum does not match!

Here's a test script: https://gist.github.com/maphew/e3a75c147cca98019cd8

Upvotes: 75

Views: 68885

Answers (3)

hi2meuk
hi2meuk

Reputation: 2024

Use the logging system to handle the error output.

I'll use requests connection error as an example use case, it generates a cascade of 3 exceptions each with a lengthy traceback. Only the final error message is of real importance - the called service is refusing, I don't need to know 3 pages of traceback from my client app!

  1. Define a custom error class
# file: custom_exceptions.py

class Error(Exception):
    """This class should be used where traceback should not be logged"""
    pass
  1. In the inner function, catch the exception and cast it to the custom error class
    try:
        response = requests.get(url, auth=auth, headers=headers, timeout=TIMEOUT)
    except requests.exceptions.ConnectionError as e:
        raise custom_exceptions.Error(e)
  1. Handle the custom exception and unexpected exceptions differently in the caller function
    except custom_exceptions.Error as e:    # any custom error based on the custom Error class
        if LOGLEVEL=logging.DEBUG:
            logger.exception(e)     # with traceback
        else:
            logger.error(e)         # just the exception and message
        exit()
    except Exception as e:
        logger.exception(e)         # with traceback
        exit()

The resultant log message - all the detail you need for this error scenario:

2021-07-23 10:58:27,545 [ERROR] HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /next (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fc54f11f9d0>: Failed to establish a new connection: [Errno 111] Connection refused'))

Upvotes: 1

Reut Sharabani
Reut Sharabani

Reputation: 31339

The short way is using the sys module and use this command:

sys.tracebacklimit = 0

Use your flag to determine the behaviour.

Example:

>>> import sys
>>> sys.tracebacklimit=0
>>> int('a')
ValueError: invalid literal for int() with base 10: 'a'

The nicer way is to use and exception hook:

def exception_handler(exception_type, exception, traceback):
    # All your trace are belong to us!
    # your format
    print "%s: %s" % (exception_type.__name__, exception)

sys.excepthook = exception_handler

Edit:

If you still need the option of falling back to the original hook:

def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
    if _your_debug_flag_here:
        debug_hook(exception_type, exception, traceback)
    else:
        print "%s: %s" % (exception_type.__name__, exception)

Now you can pass a debug hook to the handler, but you'll most likely want to always use the one originated in sys.excepthook (so pass nothing in debug_hook). Python binds default arguments once in definition time (common pitfall...) which makes this always work with the same original handler, before replaced.

Upvotes: 100

GingerPlusPlus
GingerPlusPlus

Reputation: 5616

try:
    pass # Your code here
except Exception as e:
    if debug:
        raise # re-raise the exception
              # traceback gets printed
    else:
        print("{}: {}".format(type(e).__name__, e))

Upvotes: 6

Related Questions