"Python 3.7 AsyncIO Example" from the documentation does not appear to be working

Hello!

I’m making a chatbot based on Python 3.7.5 and AsyncIO on Linux. In order to add a REST-like interface to it, I want to use Sanic. The chatbot is plugin-based, which means the Sanic code I’ll be writing for it is in a file which is loaded using importlib after the chatbot has been started (this in itself is not a problem).

Since I already have working code which starts an AsyncIO loop, I predictably get an error if I try to use the first Sanic code example from the documentation as a plugin in my chatbot software:

RuntimeError: Cannot run the event loop while another loop is running

Therefore, I decided to try to instead use the example from https://sanic.readthedocs.io/en/latest/sanic/asyncio_python37.html, since it says:

This example shows how to use sanic with Python 3.7, to be precise: how to retrieve an asyncio server instance

This seems to be what I need (please tell me if I’m wrong).

So, without involving my chatbot at all for starters, I tried to get the example on that page running, but I failed to do so. First, it complains about /tmp/sanic.sock not existing; that I can fix by adding:

except FilenotfoundError:
pass

But then, I get a trickier error:

$ python webhook2.py
Traceback (most recent call last):
File “webhook2.py”, line 37, in
loop.run_until_complete(srv.start_serving())
AttributeError: ‘AsyncioServer’ object has no attribute ‘start_serving’

So, basically, I have two questions:

  1. Am I barking up the wrong tree with trying to get this example to run? The problem I’m hoping it will solve for me, is getting Sanic to run when non-Sanic code has previously already started an AsyncIO loop.
  2. If I am barking up the right tree, then what’s wrong with the code in the example at https://sanic.readthedocs.io/en/latest/sanic/asyncio_python37.html? My code is copy-and-pasted from that example, with the addition of the “except FilenotfoundError” part I talked about above.

The try/finally block definitely needs an except in it, so that’s a problem.

Are you using uvloop which does not support the example?

No, I’m not using uvloop. I tried just copy and pasting the example into a file, and it gave me these errors.

take a look here: https://github.com/huge-success/sanic/issues/1469

It appears the example needs to be more fully updated.

I see, thank you. I guess I’ll have to keep looking for another solution to my problem then - I still need to be able to use Sanic with code that has already started doing asyncio stuff before Sanic comes into the picture.

This seems to be a bit of a trend with some async libraries I’ve looked at, like Sanic and aiogram. They want to be the “star” of the show, they don’t play too well with other asyncio code. I mean, the Sanic webhook stuff would only be one of many async libraries my project will use, and they can’t all be the ones to start the loop.

I don’t disagree; Sanic serves a purpose. It sounds like you really want a very lightweight http process - which would also help as you could ditch the routing and some other ‘nice to haves’ that Sanic implements.

Of course if you can make Sanic work, great, but there may be a better tool for the job in your case.

Also while I’m answering now, someone else may come back to you with a better solution that works within Sanic!

Right - perhaps I should look around for something lighter - all I really need is a way to create a webhook / REST API thingie that works with AsyncIO. If anybody has any recommendations, I’m all ears.

straight aiohttp might be the way to go…

1 Like

You just completely solved my problem with that suggestion - thank you! I didn’t know that aiohttp (which I’m already using in my project as a client) could also be used as a server.

For posterity, here’s my working code; the azurabot parts are the things that are specific to my chatbot project:

"""aiohttp-based webhook.                                   
"""

from aiohttp import web

from azurabot.interface.asyncinterface import AsyncInterface

routes = web.RouteTableDef()


@routes.get("/")
async def hello(request):
    return web.Response(text="Hello world!")


class Plugin(AsyncInterface):

    async def run(self):
        app = web.Application()
        app.add_routes(routes)
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, "0.0.0.0", 8080)
        await site.start()