Async exception handling (#3731)

* remove unused exception

* add logging

* disable pylint broad-except

* add exception handler

* fix lint

* update log output

* change log message in async with exc_info

* Add exc_info to asyncio exception handler
This commit is contained in:
Pascal Vizeli 2016-10-08 02:20:39 +02:00 committed by Paulus Schoutsen
parent 1c24018fbb
commit f1e5d32ef5
2 changed files with 30 additions and 2 deletions

View File

@ -136,6 +136,7 @@ class HomeAssistant(object):
self.loop = loop or asyncio.get_event_loop() self.loop = loop or asyncio.get_event_loop()
self.executor = ThreadPoolExecutor(max_workers=5) self.executor = ThreadPoolExecutor(max_workers=5)
self.loop.set_default_executor(self.executor) self.loop.set_default_executor(self.executor)
self.loop.set_exception_handler(self._async_exception_handler)
self.pool = pool = create_worker_pool() self.pool = pool = create_worker_pool()
self.bus = EventBus(pool, self.loop) self.bus = EventBus(pool, self.loop)
self.services = ServiceRegistry(self.bus, self.add_job, self.loop) self.services = ServiceRegistry(self.bus, self.add_job, self.loop)
@ -318,6 +319,25 @@ class HomeAssistant(object):
self.state = CoreState.not_running self.state = CoreState.not_running
self.loop.stop() self.loop.stop()
# pylint: disable=no-self-use
def _async_exception_handler(self, loop, context):
"""Handle all exception inside the core loop."""
message = context.get('message')
if message:
_LOGGER.warning(
"Error inside async loop: %s",
message
)
# for debug modus
exception = context.get('exception')
if exception is not None:
exc_info = (type(exception), exception, exception.__traceback__)
_LOGGER.debug(
"Exception inside async loop: ",
exc_info=exc_info
)
class EventOrigin(enum.Enum): class EventOrigin(enum.Enum):
"""Represent the origin of an event.""" """Represent the origin of an event."""

View File

@ -1,6 +1,7 @@
"""Asyncio backports for Python 3.4.3 compatibility.""" """Asyncio backports for Python 3.4.3 compatibility."""
import concurrent.futures import concurrent.futures
import threading import threading
import logging
from asyncio import coroutines from asyncio import coroutines
from asyncio.futures import Future from asyncio.futures import Future
@ -13,6 +14,9 @@ except ImportError:
ensure_future = async ensure_future = async
_LOGGER = logging.getLogger(__name__)
def _set_result_unless_cancelled(fut, result): def _set_result_unless_cancelled(fut, result):
"""Helper setting the result only if the future was not cancelled.""" """Helper setting the result only if the future was not cancelled."""
if fut.cancelled(): if fut.cancelled():
@ -111,10 +115,12 @@ def run_coroutine_threadsafe(coro, loop):
try: try:
# pylint: disable=deprecated-method # pylint: disable=deprecated-method
_chain_future(ensure_future(coro, loop=loop), future) _chain_future(ensure_future(coro, loop=loop), future)
# pylint: disable=broad-except
except Exception as exc: except Exception as exc:
if future.set_running_or_notify_cancel(): if future.set_running_or_notify_cancel():
future.set_exception(exc) future.set_exception(exc)
raise else:
_LOGGER.warning("Exception on lost future: ", exc_info=True)
loop.call_soon_threadsafe(callback) loop.call_soon_threadsafe(callback)
return future return future
@ -158,10 +164,12 @@ def run_callback_threadsafe(loop, callback, *args):
"""Run callback and store result.""" """Run callback and store result."""
try: try:
future.set_result(callback(*args)) future.set_result(callback(*args))
# pylint: disable=broad-except
except Exception as exc: except Exception as exc:
if future.set_running_or_notify_cancel(): if future.set_running_or_notify_cancel():
future.set_exception(exc) future.set_exception(exc)
raise else:
_LOGGER.warning("Exception on lost future: ", exc_info=True)
loop.call_soon_threadsafe(run_callback) loop.call_soon_threadsafe(run_callback)
return future return future