Adding doctests. Also added a logging filter.
[python_utils.git] / bootstrap.py
index 7f63dbb52259375305730f8adc952b2766b71a53..bf5d91f9eb3a42cd2432d493b74aba436e85e640 100644 (file)
@@ -42,6 +42,11 @@ original_hook = sys.excepthook
 
 
 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)
@@ -57,11 +62,11 @@ def handle_uncaught_exception(exc_type, exc_value, exc_tb):
             # 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:
@@ -69,12 +74,21 @@ def handle_uncaught_exception(exc_type, exc_value, exc_tb):
 
 
 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__
@@ -83,8 +97,9 @@ def initialize(entry_point):
         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
@@ -102,8 +117,8 @@ def initialize(entry_point):
             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:
@@ -121,6 +136,8 @@ def initialize(entry_point):
                      f'child system: {cstime}s\n'
                      f'machine uptime: {elapsed_time}s\n'
                      f'walltime: {walltime}s')
+
+        # 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: