Skip to content

Commit

Permalink
Provide global exception handling through sys.excepthook
Browse files Browse the repository at this point in the history
Catch unhandled exceptions globally through the except hook. Log these exceptions to file, ideally using the existing logger file handler. In case this handler does not exist, also output to a standard Python temporary file path, which avoids the need to load libraries to identify the user app directory.
  • Loading branch information
yoda-vid committed Apr 26, 2021
1 parent 5d7c8df commit 5ce023a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/release/release_v1.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Code base and docs
- Python APIs
- Previously Python APIs compatible with both Python 2 and 3 have been used when possible, but much of the package requires Python 3, and testing has been on Python >= 3.6
- For a more consistent and modern codebase, we are initiating use of Python 3 APIs such as `pathlib` and specifically 3.6+ features such as f-strings
- Unhandled exceptions are now logged (saved to a temp file if caught before logging is set up)
- More links to external packages in API docs
- Instructions on building the API docs
- `Blobs` and `Image5d` are being migrated to class structures for better encapsulation and additional metadata
Expand Down
34 changes: 34 additions & 0 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
"""

import logging
import multiprocessing
import os
import platform
import subprocess
import sys
import tempfile

#: str: Name of Conda or Venv environment
ENV_NAME = "mag"
Expand Down Expand Up @@ -126,6 +128,35 @@ def launch_magmap():
visualizer.main()


def log_uncaught_exception(exc_type, exc, trace):
"""Handle uncaught exceptions globally with logging.
Args:
exc_type: Exception class.
exc: Exception instance.
trace: Traceback object.
Returns:
"""
logger = logging.getLogger()
if not (any([isinstance(h, logging.StreamHandler)
for h in logger.handlers])):
# add stream handler to output to terminal if not set up yet
logger.addHandler(logging.StreamHandler())

# log to temp file in case file logging has not been set up yet,
# in additional to any existing log file handler
log_file = tempfile.NamedTemporaryFile(
prefix="magellanmapper_error_", suffix=".log", delete=False)
logger.addHandler(logging.FileHandler(log_file.name))

# log the exception
logger.critical(
"Unhandled exception. Additional log saved to: %s", log_file.name,
exc_info=(exc_type, exc, trace))


def main():
"""Launch MagellanMapper with environment activation.
Expand Down Expand Up @@ -197,6 +228,9 @@ def main():
# support multiprocessing in frozen environments, necessary for Windows;
# no effect on other platforms or non-frozen environments
multiprocessing.freeze_support()

# log any unhandled exception
sys.excepthook = log_uncaught_exception

# start MagellanMapper
print("Starting MagellanMapper run script...")
Expand Down

0 comments on commit 5ce023a

Please sign in to comment.