commit: 449b7b9f30c869781f7012ca53e9cda4efef6f9b Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Thu Apr 12 19:28:47 2018 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Thu Apr 12 19:28:47 2018 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=449b7b9f
Implement AbstractEventLoop.call_exception_handler (bug 649588) When using asyncio with _PortageEventLoopPolicy, this method is required in order to avoid a NotImplementedError if a coroutine raises an unexpected exception. Bug: https://bugs.gentoo.org/649588 pym/portage/util/_eventloop/EventLoop.py | 68 ++++++++++++++++++++++++++++++++ pym/portage/util/futures/unix_events.py | 2 + 2 files changed, 70 insertions(+) diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 12c199c76..13ce5478e 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -9,6 +9,7 @@ import os import select import signal import sys +import traceback try: import fcntl @@ -842,6 +843,73 @@ class EventLoop(object): close() self._poll_obj = None + def default_exception_handler(self, context): + """ + Default exception handler. + + This is called when an exception occurs and no exception + handler is set, and can be called by a custom exception + handler that wants to defer to the default behavior. + + The context parameter has the same meaning as in + `call_exception_handler()`. + + @param context: exception context + @type context: dict + """ + message = context.get('message') + if not message: + message = 'Unhandled exception in event loop' + + exception = context.get('exception') + if exception is not None: + exc_info = (type(exception), exception, exception.__traceback__) + else: + exc_info = False + + log_lines = [message] + for key in sorted(context): + if key in {'message', 'exception'}: + continue + value = context[key] + if key == 'source_traceback': + tb = ''.join(traceback.format_list(value)) + value = 'Object created at (most recent call last):\n' + value += tb.rstrip() + elif key == 'handle_traceback': + tb = ''.join(traceback.format_list(value)) + value = 'Handle created at (most recent call last):\n' + value += tb.rstrip() + else: + value = repr(value) + log_lines.append('{}: {}'.format(key, value)) + + logging.error('\n'.join(log_lines), exc_info=exc_info) + os.kill(os.getpid(), signal.SIGTERM) + + def call_exception_handler(self, context): + """ + Call the current event loop's exception handler. + + The context argument is a dict containing the following keys: + + - 'message': Error message; + - 'exception' (optional): Exception object; + - 'future' (optional): Future instance; + - 'handle' (optional): Handle instance; + - 'protocol' (optional): Protocol instance; + - 'transport' (optional): Transport instance; + - 'socket' (optional): Socket instance; + - 'asyncgen' (optional): Asynchronous generator that caused + the exception. + + New keys may be introduced in the future. + + @param context: exception context + @type context: dict + """ + self.default_exception_handler(context) + def get_debug(self): """ Get the debug mode (bool) of the event loop. diff --git a/pym/portage/util/futures/unix_events.py b/pym/portage/util/futures/unix_events.py index 6fcef45fa..5434cd942 100644 --- a/pym/portage/util/futures/unix_events.py +++ b/pym/portage/util/futures/unix_events.py @@ -48,6 +48,8 @@ class _PortageEventLoop(events.AbstractEventLoop): self.remove_writer = loop.remove_writer self.run_in_executor = loop.run_in_executor self.time = loop.time + self.default_exception_handler = loop.default_exception_handler + self.call_exception_handler = loop.call_exception_handler self.set_debug = loop.set_debug self.get_debug = loop.get_debug
