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
 

Reply via email to