I have an experimental library where I manage the lifecycle of event loops
for purposes of allowing async routines to operate inside a classical
generator (among other things). I create a new event loop for each use of
my routine and upon completion or GC this event loop is closed.
My recent testing has left me a bit perplexed as to the right way to deal
with KeyboardInterrupt and possibly other BaseException exceptions that
halt execution of an event loop. I've read a few posts on this list
suggesting that the basic rule of thumb employed within asyncio itself, is
to only catch Exception because BaseException is generally considered
unrecoverable (or coro protocol as in GeneratorExit). However I seem to
have found several instances where this rule is not followed and in the
except clause, further calls are made that expect the event loop to be in
good standing.
Because I close the event loop when my routine is done/interrupted the
eventual GeneratorExit exception thrown by the garbage collector triggers
some cleanup/close routines in asyncio itself (as well as aiohttp) that
eventually find themselves staring at RuntimeError('Event loop is closed').
My question to the community is if I'm witnessing bugs in these places
where naked except clauses are used to perform cleanup/closing actions on
an event loop or if I'm fundamentally doing something wrong. I hope it's
not the latter as I've been pretty happy with the results of my experiment
baring these corner cases.
[Code in question is here:
https://github.com/mayfield/cellulario/blob/master/cellulario/iocell.py]
Example traceback..
Traceback (most recent call last):
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 692, in _create_connection_transport
yield from waiter
GeneratorExit
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/mayfield/project/syndicate/syndicate/adapters/async.py",
line 74, in request
result = yield from asyncio.wait_for(r, timeout, loop=self.loop)
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/tasks.py",
line 359, in wait_for
return (yield from fut)
File
"/usr/local/lib/python3.5/site-packages/aiohttp-0.18.3-py3.5-macosx-10.10-x86_64.egg/aiohttp/client.py",
line 456, in __iter__
resp = yield from self._coro
File
"/usr/local/lib/python3.5/site-packages/aiohttp-0.18.3-py3.5-macosx-10.10-x86_64.egg/aiohttp/client.py",
line 173, in _request
conn = yield from self._connector.connect(req)
File
"/usr/local/lib/python3.5/site-packages/aiohttp-0.18.3-py3.5-macosx-10.10-x86_64.egg/aiohttp/connector.py",
line 289, in connect
transport, proto = yield from self._create_connection(req)
File
"/usr/local/lib/python3.5/site-packages/aiohttp-0.18.3-py3.5-macosx-10.10-x86_64.egg/aiohttp/connector.py",
line 557, in _create_connection
server_hostname=hinfo['hostname'] if sslcontext else None)
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 669, in create_connection
sock, protocol_factory, ssl, server_hostname)
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 694, in _create_connection_transport
transport.close()
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/selector_events.py",
line 566, in close
self._loop.call_soon(self._call_connection_lost, None)
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 453, in call_soon
handle = self._call_soon(callback, args)
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 462, in _call_soon
self._check_closed()
File
"/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py",
line 289, in _check_closed
raise RuntimeError('Event loop is closed')