This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: asyncio does not support yielding from recvfrom (socket/udp)
Type: Stage:
Components: asyncio Versions: Python 3.5
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: gvanrossum, martin.panter, sbstp, vstinner, yselivanov
Priority: normal Keywords:

Created on 2016-02-20 22:50 by sbstp, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (7)
msg260580 - (view) Author: Simon Bernier St-Pierre (sbstp) * Date: 2016-02-20 22:50
I want to receive data on a UDP socket that was bound, without blocking the event loop. I've looked through the asyncio docs, and I haven't found a way of doing that using the coroutine API (yield from/await).

There is a sock_recv method on BaseEventLoop which is a coroutine, it seems like sock_recvfrom was never implemented.

I don't have a patch for this right now, I wanted to know what people thought of adding support for this.
msg260582 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2016-02-20 23:59
You should be able to do this by calling create_datagram_endpoint(), passing it a custom DatagramProtocol subclass whose datagram_received() stores the data in the result of a Future. You can then wait for the Future to wait for the data (assuming it ever arrives :-).
msg260585 - (view) Author: Simon Bernier St-Pierre (sbstp) * Date: 2016-02-21 00:23
That could work. I came up with this

class MyProtocol(aio.DatagramProtocol):
    def __init__(self, fut):
        self._fut = fut

    def datagram_received(self, data, addr):
        self.fut.set_result((data, addr))

fut = aio.Future()
loop.create_datagram_endpoint(lambda: MyProtocol(fut), ...)
yield from fut

1. Is there a better way of sharing the future between the protocol and the main function?
2. This might be inefficient because I have to create a new endpoint every time I want to receive a packet. I might be able to implement a scheme where I give the protocol a new future after every packet, but it's kind of cumbersome.

If I wrote the patch to make sock_recvfrom work, can it get merged or must it go through the PEP process?
msg260586 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2016-02-21 00:36
I won't make you go through the PEP process, but I do think it's a bad
idea to add this. After all datagrams aren't guaranteed to arrive, so,
whil I don't know what protocol you're trying to implement, I think
you're probably better off writing the whole thing as a
DatagramProtocol subclass that handles all incoming packets.

If you need help writing your app's code it's probably better to ask
around on a mailing list.
msg260588 - (view) Author: Simon Bernier St-Pierre (sbstp) * Date: 2016-02-21 00:47
I want to have a loop that receives data like this:

socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket.bind(('0.0.0.0', port))
socket.setblocking(False)
while True:
    data, addr = await loop.sock_recvfrom(sock, 4096)
    # process packet

It's pretty similar to what the blocking code would look like, but it allows me to keep everything on a single thread without blocking. It could be done with the Protocol API, but I'd rather use the shiny new async/await API.
msg260596 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2016-02-21 07:41
If your event loop supports it, maybe you could use add_reader() etc as a workaround (roughly based off a different function of my own; this version completely untested):

async def sock_recvfrom(nonblocking_sock, *pos, loop, **kw):
    while True:
        try:
            return nonblocking_sock.recvfrom(*pos, **kw)
        except BlockingIOError:
            future = Future(loop=loop)
            loop.add_reader(nonblocking_sock.fileno(), future.set_result, None)
            try:
                await future
            finally:
                loop.remove_reader(nonblocking_sock.fileno())

I’m not very experienced with asyncio, but I imagine having general-purpose loop.wait_readable(file_descriptor) etc methods would make writing these kind of functions easier.
msg260614 - (view) Author: Simon Bernier St-Pierre (sbstp) * Date: 2016-02-21 15:40
I created a patch for it on the asyncio github repo.

https://github.com/python/asyncio/pull/321
History
Date User Action Args
2022-04-11 14:58:27adminsetgithub: 70583
2016-02-22 15:46:16sbstpsetstatus: open -> closed
2016-02-21 15:40:38sbstpsetmessages: + msg260614
2016-02-21 07:41:02martin.pantersetnosy: + martin.panter
messages: + msg260596
2016-02-21 00:47:42sbstpsetmessages: + msg260588
2016-02-21 00:36:37gvanrossumsetmessages: + msg260586
2016-02-21 00:23:49sbstpsetmessages: + msg260585
2016-02-20 23:59:24gvanrossumsetmessages: + msg260582
2016-02-20 22:50:29sbstpcreate