Fix portage commands to exit immediately for any unhandled
exceptions that are raised while the asyncio event loop is running
without a tty. If we have a tty then start the debugger,
since in might aid in diagnosis of the problem.

In order to avoid potential interference with API consumers, do not
call set_exception_handler unless portage._internal_caller is True.

Bug: https://bugs.gentoo.org/658684
---
 pym/portage/util/_eventloop/asyncio_event_loop.py | 31 +++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/pym/portage/util/_eventloop/asyncio_event_loop.py 
b/pym/portage/util/_eventloop/asyncio_event_loop.py
index c07b71103c..ea0e03b23b 100644
--- a/pym/portage/util/_eventloop/asyncio_event_loop.py
+++ b/pym/portage/util/_eventloop/asyncio_event_loop.py
@@ -2,7 +2,9 @@
 # Distributed under the terms of the GNU General Public License v2
 
 import os
+import pdb
 import signal
+import sys
 
 try:
        import asyncio as _real_asyncio
@@ -53,6 +55,35 @@ class AsyncioEventLoop(_AbstractEventLoop):
                self.get_debug = loop.get_debug
                self._wakeup_fd = -1
 
+               if portage._internal_caller:
+                       
loop.set_exception_handler(self._internal_caller_exception_handler)
+
+       @staticmethod
+       def _internal_caller_exception_handler(loop, context):
+               """
+               An exception handler which drops to a pdb shell if std* streams
+               refer to a tty, and otherwise kills the process with SIGTERM.
+
+               In order to avoid potential interference with API consumers, 
this
+               implementation is only used when portage._internal_caller is 
True.
+               """
+               loop.default_exception_handler(context)
+               if 'exception' in context:
+                       # If we have a tty then start the debugger, since in 
might
+                       # aid in diagnosis of the problem. If there's no tty, 
then
+                       # exit immediately.
+                       if all(s.isatty() for s in (sys.stdout, sys.stderr, 
sys.stdin)):
+                               pdb.set_trace()
+                       else:
+                               # Normally emerge will wait for all coroutines 
to complete
+                               # after SIGTERM has been received. However, an 
unhandled
+                               # exception will prevent the interrupted 
coroutine from
+                               # completing, therefore use the default SIGTERM 
handler
+                               # in order to ensure that emerge exits 
immediately (though
+                               # uncleanly).
+                               signal.signal(signal.SIGTERM, signal.SIG_DFL)
+                               os.kill(os.getpid(), signal.SIGTERM)
+
        def _create_future(self):
                """
                Provide AbstractEventLoop.create_future() for python3.4.
-- 
2.13.6


Reply via email to