Issue 22428: asyncio: KeyboardInterrupt inside a coroutine causes AttributeError (original) (raw)
The following test script prints a KeyboardInterrupt traceback (expected), but also an AttributeError traceback (unexpected):
import asyncio @asyncio.coroutine def main(): raise KeyboardInterrupt asyncio.get_event_loop().run_until_complete(main())
Traceback (most recent call last): File "test.py", line 9, in asyncio.get_event_loop().run_until_complete(main()) File "/usr/lib/python3.4/asyncio/base_events.py", line 203, in run_until_complete self.run_forever() File "/usr/lib/python3.4/asyncio/base_events.py", line 184, in run_forever self._run_once() File "/usr/lib/python3.4/asyncio/base_events.py", line 817, in _run_once handle._run() File "/usr/lib/python3.4/asyncio/events.py", line 39, in _run self._callback(*self._args) File "/usr/lib/python3.4/asyncio/tasks.py", line 321, in _step result = next(coro) File "/usr/lib/python3.4/asyncio/tasks.py", line 103, in coro res = func(*args, **kw) File "test.py", line 6, in main raise KeyboardInterrupt KeyboardInterrupt --- Logging error --- Traceback (most recent call last): --- Logging error --- Traceback (most recent call last): Exception ignored in: <bound method Task.__del__ of Task()<exception=KeyboardInterrupt()>> Traceback (most recent call last): File "/usr/lib/python3.4/asyncio/futures.py", line 184, in del File "/usr/lib/python3.4/asyncio/base_events.py", line 725, in call_exception_handler File "/usr/lib/python3.4/logging/init.py", line 1296, in error File "/usr/lib/python3.4/logging/init.py", line 1402, in _log File "/usr/lib/python3.4/logging/init.py", line 1412, in handle File "/usr/lib/python3.4/logging/init.py", line 1482, in callHandlers File "/usr/lib/python3.4/logging/init.py", line 846, in handle File "/usr/lib/python3.4/logging/init.py", line 977, in emit File "/usr/lib/python3.4/logging/init.py", line 899, in handleError File "/usr/lib/python3.4/traceback.py", line 169, in print_exception File "/usr/lib/python3.4/traceback.py", line 153, in _format_exception_iter File "/usr/lib/python3.4/traceback.py", line 18, in _format_list_iter File "/usr/lib/python3.4/traceback.py", line 65, in _extract_tb_or_stack_iter File "/usr/lib/python3.4/linecache.py", line 15, in getline File "/usr/lib/python3.4/linecache.py", line 41, in getlines File "/usr/lib/python3.4/linecache.py", line 126, in updatecache File "/usr/lib/python3.4/tokenize.py", line 437, in open AttributeError: 'module' object has no attribute 'open'
The issue is that Task._step() calls self.set_exception() for both Exceptions and BaseExceptions, but it reraises BaseExceptions. That means that the BaseEventLoop never gets to call future.result() to clear the exception when it's a BaseException. Future.del eventually tries to log this uncleared exception, but something about the circumstances of program exit (caused by the same exception) has already cleaned up the builtins needed for logging.
Is that del violating some best practices for finalizers by calling such a complicated function? Either way, I'm not sure this case should be considered an "unretrieved exception" at all. It's propagating outside the event loop after all. Should Task._step() be setting it in the first place, if it's going to reraise it? Should it be set without _log_traceback=True somehow?