Use asyncio. Bind to a socket, set blocking False, then asyncio.run(on_new_connection(sock)).
Inside that coroutine get the loop and await loop.sock_accept(sock)
And then asyncio.create_task(on_connection_data(connection)).
The only gotcha is you need to keep a reference to that task so it doesn't get garbage collected.