From 0246761f943c6bfac2d6f2883d3472e74f2964a6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 28 Apr 2020 14:31:35 -0700 Subject: [PATCH] Log threading exceptions properly (#34789) --- homeassistant/bootstrap.py | 4 ++++ homeassistant/core.py | 2 ++ homeassistant/util/thread.py | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 homeassistant/util/thread.py diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 618a168be61..d53d86f528c 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -249,6 +249,10 @@ def async_enable_logging( logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("aiohttp.access").setLevel(logging.WARNING) + sys.excepthook = lambda *args: logging.getLogger(None).exception( + "Uncaught exception", exc_info=args # type: ignore + ) + # Log errors to a file if we have write access to file or config dir if log_file is None: err_log_path = hass.config.path(ERROR_LOG_FILENAME) diff --git a/homeassistant/core.py b/homeassistant/core.py index c799656df89..045e56ecb53 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -71,6 +71,7 @@ from homeassistant.exceptions import ( from homeassistant.util import location from homeassistant.util.async_ import fire_coroutine_threadsafe, run_callback_threadsafe import homeassistant.util.dt as dt_util +from homeassistant.util.thread import fix_threading_exception_logging from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem # Typing imports that create a circular dependency @@ -80,6 +81,7 @@ if TYPE_CHECKING: block_async_io.enable() +fix_threading_exception_logging() # pylint: disable=invalid-name T = TypeVar("T") diff --git a/homeassistant/util/thread.py b/homeassistant/util/thread.py new file mode 100644 index 00000000000..e5654e6f8c6 --- /dev/null +++ b/homeassistant/util/thread.py @@ -0,0 +1,26 @@ +"""Threading util helpers.""" +import sys +import threading +from typing import Any + + +def fix_threading_exception_logging() -> None: + """Fix threads passing uncaught exceptions to our exception hook. + + https://bugs.python.org/issue1230540 + Fixed in Python 3.8. + """ + if sys.version_info[:2] >= (3, 8): + return + + run_old = threading.Thread.run + + def run(*args: Any, **kwargs: Any) -> None: + try: + run_old(*args, **kwargs) + except (KeyboardInterrupt, SystemExit): # pylint: disable=try-except-raise + raise + except Exception: # pylint: disable=broad-except + sys.excepthook(*sys.exc_info()) + + threading.Thread.run = run # type: ignore