autobahn-python icon indicating copy to clipboard operation
autobahn-python copied to clipboard

Document onUserError

Open faerics opened this issue 6 years ago • 2 comments

I'm writing a game application using autobahn, and first I encountered this issue at the application level and then realised it is relevant to the library.

I omit the imports assuming they are obvious.

Usecase

When opening a connection (or generally firing a callback of a protocol) I would like to shedule some async task. So I write

class PlayerProtocol(WebSocketServerProtocol):
    def onOpen(self):
        asyncio.ensure_future(begin(), loop=loop)

Everything's ok, but when I occationnally break functionality of a coroutine begin()... nothing happens! No exception is raised. B-but why? I started an investigation.

The Rule

This investigation lead me to a simple rule: one should either await the task or handle the exceptions in a callback. What happens is pretty strightforward: the task is done with an exception, and as no one awaits for its result, the result (which is an exception in the case) is thrown away.

The same in the library

So I ended up with the callback that logs the exception, but glanced in a cource code and saw in asyncio/websocket.py:WebSocketAdapterProtocol these lines:

def _onOpen(self):
    res = self.onOpen()
    if yields(res):
        ensure_future(res)

This is totally the same pattern that I implemented in my app: if self.onOpen() is a function, we're done, and if it is a coroutine (roughly), we shedule it. The other callbacks are the same.

Here we have an owerwhelming knowledge that onOpen and others may be a coroutine, but the docs says nothing about it. And I could throw away dancing with callbacks and just await begin()!. This is another issue, I will open it next. But...

But what if things in onOpen go wrong? Say,

class TrappingProtocol(WebSocketServerProtocol):
    async def onOpen(self):
        raise ValueError

Then we run it and try to connect with a simple client. And..? You are right -- nothing happen. The user (a programmer) did not even notice the error.

A simplified example that should be enough to reproduce:

class TrappingProtocol(WebSocketServerProtocol):
    async def onOpen(self):
        raise ValueError

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    factory = WebSocketServerFactory()
    factory.protocol = TrappingProtocol

    coro = loop.create_server(factory, HOST, PORT)
    server = loop.run_until_complete(coro)
    try:
        print('Server created.')
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()

A solution

Actually in this case I exepect that the server crash. Now in my app I implemented a callback which just logs the exception and stops the loop. Opening to discuss whether this solution is enough and ready to PR then if you giude me a little. (never did a PR on github thuogh)

Cheers!

faerics avatar Feb 01 '19 12:02 faerics

#485 may be related.

faerics avatar Feb 01 '19 12:02 faerics

For now, pls see https://autobahn.readthedocs.io/en/latest/reference/autobahn.wamp.html?highlight=onUserError#autobahn.wamp.interfaces.ISession.onUserError

A proper documentation/example would be even better;)

oberstet avatar Apr 22 '19 10:04 oberstet