Python-socketio: Example ASGI server does not work with newer Uvicorn

Created on 28 Mar 2019  路  3Comments  路  Source: miguelgrinberg/python-socketio

Niet that I do not neccesarily expect that the example would keep working as dependencies change. However, perhaps there鈥檚 an easy fix or insightful remark that鈥檒l help me :)

Issue
The example ASGI server does not work with Uvicorn 0.6, but it does work with Uvicorn 0.3.

if __name__ == '__main__': loop = auto_loop_setup() sio.start_background_task(background_task) uvicorn.run(app, '127.0.0.1', 5000, loop=loop)

Error
[...]site-packages/uvicorn/protocols/http/httptools_impl.py:20: RuntimeWarning: coroutine 'render' was never awaited status_code: _get_status_line(status_code) for status_code in range(100, 600) RuntimeWarning: Enable tracemalloc to get the object allocation traceback 2019-03-28 16:21:14,464 ERROR Task was destroyed but it is pending!

What I think
It seems you no longer pass the loop to uvicorn. If you check for the current loop after 'auto_loop_setup()' then there is a loop, but it hasn't started yet.

Why I'm interested
I have a hardware project where a single thread can change some GPIO pins. Preferably I keep everything in asyncio: in that way I get parallelism, socket connections with a global state (GPIO pins) without locking/.

bug

Most helpful comment

I got python-socketio working together with background tasks on uvicorn. Additionally I made it work together with Starlette. I didn't see an example here, so perhaps it's a nice example to add.

import logging
import asyncio
import uvicorn
from uvicorn.loops.uvloop import uvloop_setup
from starlette.applications import Starlette
from starlette.responses import JSONResponse
import socketio

# Set some basic logging
logging.basicConfig(
    level=2,
    format="%(asctime)-15s %(levelname)-8s %(message)s"
)

# Create a basic app
sio = socketio.AsyncServer(async_mode='asgi')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)


@star_app.route('/')
async def homepage(request):
    return JSONResponse({'hello': 'world'})


@sio.on('connect')
async def connect(sid, environ):
    logging.info(f"connect {sid}")


@sio.on('message')
async def message(sid, data):
    logging.info(f"message {data}")
    # await device.set(data)


@sio.on('disconnect')
async def disconnect(sid):
    logging.info(f'disconnect {sid}')


# Set up the event loop
async def start_background_tasks():
    while True:
        logging.info(f"Background tasks that ticks every 10s.")
        await sio.sleep(10.0)


async def start_uvicorn():
    uvicorn.run(app, host='0.0.0.0', port=8000)


async def main(loop):
    bg_task = loop.create_task(start_background_tasks())
    uv_task = loop.create_task(start_uvicorn())
    await asyncio.wait([bg_task, uv_task])

if __name__ == '__main__':
    uvloop_setup()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.close()

Probably you can also add background tasks from the event handlers. That also worked in the original example, so that should still work.

If you want I can make a pull request with a sample (similar to the other samples). However, the background_task would have a slightly different API (as in: I use the asyncio way to schedule functions and not the function in python-socketio-library.

All 3 comments

It's unfortunate that uvicorn changed in a way that broke these examples, but I'll see if I can fix everything to work again. Thanks.

I got python-socketio working together with background tasks on uvicorn. Additionally I made it work together with Starlette. I didn't see an example here, so perhaps it's a nice example to add.

import logging
import asyncio
import uvicorn
from uvicorn.loops.uvloop import uvloop_setup
from starlette.applications import Starlette
from starlette.responses import JSONResponse
import socketio

# Set some basic logging
logging.basicConfig(
    level=2,
    format="%(asctime)-15s %(levelname)-8s %(message)s"
)

# Create a basic app
sio = socketio.AsyncServer(async_mode='asgi')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)


@star_app.route('/')
async def homepage(request):
    return JSONResponse({'hello': 'world'})


@sio.on('connect')
async def connect(sid, environ):
    logging.info(f"connect {sid}")


@sio.on('message')
async def message(sid, data):
    logging.info(f"message {data}")
    # await device.set(data)


@sio.on('disconnect')
async def disconnect(sid):
    logging.info(f'disconnect {sid}')


# Set up the event loop
async def start_background_tasks():
    while True:
        logging.info(f"Background tasks that ticks every 10s.")
        await sio.sleep(10.0)


async def start_uvicorn():
    uvicorn.run(app, host='0.0.0.0', port=8000)


async def main(loop):
    bg_task = loop.create_task(start_background_tasks())
    uv_task = loop.create_task(start_uvicorn())
    await asyncio.wait([bg_task, uv_task])

if __name__ == '__main__':
    uvloop_setup()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.close()

Probably you can also add background tasks from the event handlers. That also worked in the original example, so that should still work.

If you want I can make a pull request with a sample (similar to the other samples). However, the background_task would have a slightly different API (as in: I use the asyncio way to schedule functions and not the function in python-socketio-library.

I have addressed the problems with uvicorn now.

Was this page helpful?
0 / 5 - 0 ratings