classification
Title: Asyncio: GeneratorExit + strange exception
Type: Stage:
Components: asyncio Versions: Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: socketpair, yselivanov
Priority: normal Keywords:

Created on 2017-04-16 18:18 by socketpair, last changed 2017-04-16 18:19 by socketpair.

Messages (1)
msg291763 - (view) Author: Марк Коренберг (socketpair) * Date: 2017-04-16 18:18
How to reproduce: Run the following program:
=========================
import asyncio
async def handle_connection(reader, writer):
    try:
        await reader.readexactly(42)
    except BaseException as err:
        print('Interesting: %r.' % err)
        raise
    finally:
        writer.close()

loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_connection, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
try:
    loop.run_forever()
except KeyboardInterrupt:
    print('KeyboardInterrupt catched.')
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
=========================

0. Python 3.5.2
1. Connect using telnet to localhost and port 888, type one short line and press Enter.
2. Type Ctrl+C in terminal where programw is running.
3. You will see the following output:

=========================
^CKeyboardInterrupt catched.
Interesting: GeneratorExit().
Exception ignored in: <coroutine object handle_connection at 0x7fa6a1d91b48>
Traceback (most recent call last):
  File "bug.py", line 12, in handle_connection
    writer.close()
  File "/usr/lib/python3.5/asyncio/streams.py", line 306, in close
    return self._transport.close()
  File "/usr/lib/python3.5/asyncio/selector_events.py", line 591, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 567, in call_soon
    handle = self._call_soon(callback, args)
  File "/usr/lib/python3.5/asyncio/base_events.py", line 576, in _call_soon
    self._check_closed()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 356, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Task was destroyed but it is pending!
task: <Task pending coro=<handle_connection() done, defined at bug.py:4> wait_for=<Future pending cb=[Task._wakeup()]>>
=========================

This is almost canonical example of asyncio usage. So I have two questions:

1. Why coroutine is interrupted with GeneratorExit instead of CancelledError ?
2. Why something happend AFTER io loop is closed ?
3. How to code all that right ? I want to close connection on any error. Example provided is simplified code. In real code it looks like:

=====
        try:
            await asyncio.wait_for(self._handle_connection(reader, writer), 60)
        except asyncio.TimeoutError:
            writer.transport.abort()
        except asyncio.CancelledError:
            writer.transport.abort()
        except Exception:
            writer.transport.abort()
        finally:
            writer.close()
=====
History
Date User Action Args
2017-04-16 18:19:43socketpairsetnosy: + yselivanov

components: + asyncio
versions: + Python 3.5
2017-04-16 18:18:42socketpaircreate