Why is loop.create_task taking so long to execute?

Question:

I am trying to emulate the reading of a NFC chip by pressing the cmd key in a little python script that acts as a socket-io server. It’s job is to notify a client after the cmd key has been pressed. It works, but emulate_nfc_tag() takes ages to execute after i press cmd. I suspect it has to do with how create_task works. I hope someone can spot how i could optimize this code.

from aiohttp import web
import socketio
import asyncio
from pynput import keyboard
import random
import string


sio = socketio.AsyncServer(cors_allowed_origins='*',
                           async_mode='aiohttp')
app = web.Application()
sio.attach(app)


def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))


async def emulate_nfc_tag():
    print("sending out nfc id")
    await sio.emit("nfc", id_generator())


def on_press(key):
    if key == keyboard.Key.shift:
        print("here we go")
        loop.create_task(emulate_nfc_tag())
    if key == keyboard.Key.esc:
        # Stop listener
        return False


async def index(request):
    """Serve the client-side application."""
    with open('../client/dist/index.html') as f:
        return web.Response(text=f.read(), content_type='text/html')


@sio.event
def connect(sid, environ):
    print("connect ", sid)


@sio.event
def disconnect(sid):
    print('disconnect ', sid)


@sio.event
async def msg(sid, data):
    print('message ', data)
    await sio.emit("msg", "Hi back")


app.router.add_static('/assets', '../client/dist/assets')
app.router.add_get('/', index)


if __name__ == '__main__':
    # web.run_app(app)
    loop = asyncio.get_event_loop()
    listener = keyboard.Listener(on_press=on_press)
    listener.start()
    # set up aiohttp - like run_app, but non-blocking
    runner = web.AppRunner(app)
    loop.run_until_complete(runner.setup())
    site = web.TCPSite(runner)
    loop.run_until_complete(site.start())
    # add more stuff to the loop
    loop.run_forever()
Asked By: Ben

||

Answers:

The handlers that you configure for your keyboard run on a background thread and not in the thread that holds your asyncio application. You are calling the create_task() function from this handler, which is invalid because this function must be called from the asyncio thread.

Instead, you may want to try with the run_coroutine_threadsafe(), which is specifically designed for your use case in which you want to start a coroutine in the asyncio thread, but doing it from another thread.

Answered By: Miguel Grinberg