Please can you review my standard exception handler for Python 3.x. It is located under the # +=+=
line. The code above the line is to create an exception and so the code is code complete and runs standalone.
The code is just a standard exception formatter, so users are presented with easier to read messages rather than stack traces. This is a common idiom in many Python projects. But many / all don't format to the extent I have.
I have searched a lot, but I can't find anything better. I expected to find something on GitHub, but was unfruitful. If you find a way to improve this, please post it here so that I and others will know.
import inspect
import os
import sys
import traceback
if __name__ == '__main__':
try:
x = 42/0
# +=+=+=+=+=+=+=+=+=+=+=+=+=
except KeyboardInterrupt as e: # Ctrl-C
raise e
except SystemExit as e: # sys.exit()
raise e
except Exception as e :
exc_type, exc_obj, exc_tb = sys.exc_info()
scriptName = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
functionName = inspect.currentframe().f_code.co_name
print('=======================================================================================================')
print('Unhandled exception: ', exc_type, scriptName, 'at line', exc_tb.tb_lineno)
print(' in function', functionName)
print(' ', str(e))
traceback.print_exc()
sys.exit(1)
-
3\$\begingroup\$ Having said that, I think you got a nice idea going and I'm totally incorporating your idea into my current project. \$\endgroup\$Mast– Mast ♦2021年01月07日 09:25:41 +00:00Commented Jan 7, 2021 at 9:25
-
\$\begingroup\$ Please let me know if you ever make improvements, no mater how tiny. For instance, my PHP project email me on exception \$\endgroup\$Mawg– Mawg2021年07月02日 06:06:59 +00:00Commented Jul 2, 2021 at 6:06
1 Answer 1
Rather than using
as e
thenraise e
you can just omite
.except KeyboardInterrupt as e: raise e
except KeyboardInterrupt: raise
Rather than specifying an except block for
KeyboardInterrupt
andSystemExit
you can just specify them in the sameexcept
by passing a tuple.except KeyboardInterrupt: raise except SystemExit: raise
except (KeyboardInterrupt, SystemExit): raise
Both
KeyboardInterrupt
andSystemExit
are not subclasses ofException
so you do not need to handle them. The exceptions will propagate like normal.You may want to familiarize yourself with the exception hierarchy.
You can get all the information that
sys.exc_info()
gives frome
.exc_type, exc_obj, exc_tb = sys.exc_info()
exc_type, exc_obj, exc_tb = type(e), e, e.__traceback__
You can get the current frame from
exc_tb
rather thaninspect.currentframe()
. This makes the code more portable if you ever decide to move the code into a function to format tracebacks.frame = inspect.currentframe()
frame = exc_tb.tb_frame
Please follow PEP 8 and name variables with
snake_case
. It is very easy to see the code you've taken from the Python docs and the code you've written yourself / taken from somewhere else.If you're going to hard code the
==
at least use*
to build it the size you want.print('=======================================================================================================')
print('=' * 103)
Better yet just print to the size of the terminal.
print('=' * os.get_terminal_size().columns)
I would prefer to use f-strings if available.
print('Unhandled exception: ', exc_type, script_name, 'at line', exc_tb.tb_lineno)
print(f'Unhandled exception: {exc_type} {script_name} at line {exc_tb.tb_lineno}')
if __name__ == '__main__':
try:
x = 42/0
except Exception as e:
exc_tb = e.__traceback__
script_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
function_name = exc_tb.tb_frame.f_code.co_name
print('=' * os.get_terminal_size().columns)
print(f'Unhandled exception: {type(e)} {script_name} at line {exc_tb.tb_lineno}')
print(f' in function {function_name}')
print(f' {e}')
traceback.print_exc()
raise SystemExit(1)
-
\$\begingroup\$ Wow! Some incredible feedback. I can see that I still have a lot to learn. I will hold off awarding the answer for a day or two, although I doubt that there will be a better answer. I still can't figure out why there isn't something like this on GitHub, maybe with the option to email the developer on exception (something for me to add. Maybe I will also add
pprint.pprint(locals())
andpprint.pprint(globals())
). \$\endgroup\$Mawg– Mawg2021年01月07日 16:43:53 +00:00Commented Jan 7, 2021 at 16:43 -
2\$\begingroup\$ @MawgsaysreinstateMonica I would assume one of the following is why 1 most authors assume the user will know how to read Python trackbacks 2 many authors don't know how to format tracebacks - how many people know
__traceback__
even exists never-mindtb_frame
? 3 The application useslogging
rather than exceptions to inform the user of errors and function state. ⏣ I'd assume a combination of 1&2 leave many projects with simpleexcept
blocks if anything. Then because 3 is much easier and expressive than 2 it's just not worth it to most people. That said pytest has something similar. \$\endgroup\$2021年01月07日 16:52:28 +00:00Commented Jan 7, 2021 at 16:52 -
1