def handle_uncaught_exception(exc_type, exc_value, exc_tb):
+ """
+ Top-level exception handler for exceptions that make it past any exception
+ handlers in the python code being run. Logs the error and stacktrace then
+ maybe attaches a debugger.
+ """
global original_hook
msg = f'Unhandled top level exception {exc_type}'
logger.exception(msg)
# stdin or stderr is redirected, just do the normal thing
original_hook(exc_type, exc_value, exc_tb)
else:
- # a terminal is attached and stderr is not redirected, debug.
+ # a terminal is attached and stderr is not redirected, maybe debug.
+ import traceback
+ traceback.print_exception(exc_type, exc_value, exc_tb)
if config.config['debug_unhandled_exceptions']:
- import traceback
import pdb
- traceback.print_exception(exc_type, exc_value, exc_tb)
logger.info("Invoking the debugger...")
pdb.pm()
else:
def initialize(entry_point):
+ """
+ Remember to initialize config, initialize logging, set/log a random
+ seed, etc... before running main.
- """Remember to initialize config and logging before running main."""
+ """
@functools.wraps(entry_point)
def initialize_wrapper(*args, **kwargs):
+
+ # Hook top level unhandled exceptions, maybe invoke debugger.
if sys.excepthook == sys.__excepthook__:
sys.excepthook = handle_uncaught_exception
+
+ # Try to figure out the name of the program entry point. Then
+ # parse configuration (based on cmdline flags, environment vars
+ # etc...)
if (
'__globals__' in entry_point.__dict__ and
'__file__' in entry_point.__globals__
else:
config.parse(None)
+ # Initialize logging... and log some remembered messages from
+ # config module.
logging_utils.initialize_logging(logging.getLogger())
-
config.late_logging()
# Allow programs that don't bother to override the random seed
logger.debug(msg)
random.seed(random_seed)
+ # Do it, invoke the user's code. Pay attention to how long it takes.
logger.debug(f'Starting {entry_point.__name__} (program entry point)')
-
ret = None
import stopwatch
with stopwatch.Timer() as t:
f'child system: {cstime}s\n'
f'machine uptime: {elapsed_time}s\n'
f'walltime: {walltime}s')
- if ret != 0:
- logger.info(f'Exit {ret}')
+
+ # If it doesn't return cleanly, call attention to the return value.
+ if ret is not None and ret != 0:
+ logger.error(f'Exit {ret}')
else:
logger.debug(f'Exit {ret}')
sys.exit(ret)