Python code that should error from asyncio.wait and be caught in a try clause is not being caught

StackOverflow https://stackoverflow.com/questions/23164701

Question

so I have an event loop which will run_until_complete my accept_connection method

@asyncio.coroutine
def accept_connection(self):
    assert self.server_socket is not None

    while True:
        client, addr = yield from self.loop.sock_accept(self.server_socket)
        asyncio.async(self.handle_connection(client, addr))

my handle_connection method looks like this

 def handle_connection(self, client, addr):
     #removed error checking

    while True:
        try:
            yield from asyncio.wait([con.handle_read_from_connection()], timeout=5.0)
        except (AssertionError, PacketException):
            print("Invalid packet detected!")

finally my handle_read_from_connection (currently) looks like this:

@asyncio.coroutine
def handle_read_from_connection(self):
    raise PacketException("hello")

therefore this method should always raise an error and hit the except block of the try catch statement and print invalid packet detected. Instead what happens is I get a traceback!

future: Task(<handle_read_from_connection>)<exception=PacketException('hello',)>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/tasks.py", line 283, in _step
    result = next(coro)
  File "/some_path.py", line 29, in handle_read_from_connection
    raise PacketException("hello")
GameProtocol.GameProtocol.PacketException: hello

does anyone know what going on here? why is the try catch not working? and how can I get it so we can catch these errors

Was it helpful?

Solution

You need to use the returned values from asyncio.wait():

import asyncio

class Error(Exception):
    pass

@asyncio.coroutine
def main():
    try:
        done, pending = yield from asyncio.wait([raise_exception()], timeout=1)
        assert not pending
        future, = done # unpack a set of length one
        print(future.result()) # raise an exception or use future.exception()
    except Error:
        print('got exception', flush=True)
    else:
        print('no exception', flush=True)

@asyncio.coroutine
def raise_exception(): # normally it is a generator (yield from)
                       #  or it returns a Future
    raise Error("message")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Output

got exception

The current implementation of asyncio.coroutine assumes that if a function is not a generator (like in your case) then it returns a Future and thus it should be converted to be a generator therefore calling raise_exception() doesn't raise an exception because it just creates the generator object (the coroutine).

Then asyncio.wait() yields it and some equivalent of future.set_exception(exception) is done.

To avoid the error output that you see; you need to consume the exception either by calling future.result() or future.exception() directly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top