0

I am trying to create some code in python that will put data from a generator (currently a simple counting loop but will be a sensor data at some point) and place it in a queue. Once in a queue i want to pull data off it and send it over a TCP connection. This is a great time to use asyncio but I am doing something wrong.

Currently, the script will process all the numbers and does not return anything. Ideally I would want to make sure I have something in the queue so it never empties and send a set amount of data over say like 5 numbers everytime. How can I achieve this?

import asyncio
import random

class responder():
    
    def __init__(self, parent=None):    
        super().__init__()



    async def produce(self,queue, n):
        for x in range(n):
            # produce an item
            print('producing {}/{}'.format(x, n))
            # simulate i/o operation using sleep
            await asyncio.sleep(random.random())
            item = str(x)
            # put the item in the queue
            await queue.put(item)

    async def consume(self,queue):
        while True:
            # wait for an item from the producer
            item = await queue.get()

            # process the item
            print('consuming {}...'.format(item))
            # simulate i/o operation using sleep
            await asyncio.sleep(random.random())

            # Notify the queue that the item has been processed
            queue.task_done()

    async def run(self,n):
        queue = asyncio.Queue()
        # schedule the consumer
        self.consumer = asyncio.ensure_future(self.consume(queue))
        # run the producer and wait for completion
        await self.produce(queue, n)
        # wait until the consumer has processed all items
        await queue.join()
        # the consumer is still awaiting for an item, cancel it
        self.consumer.cancel()

    async def handle_echo(self,reader, writer):
        data = await reader.read(100)
        message = data.decode()
        addr = writer.get_extra_info('peername')
        print("Received %r from %r" % (message, addr))
        if (message == 'START_RUN'):
            data = await self.run(10) 
            print("Send: %i" % data)
            writer.write(data)
            await writer.drain()
        else: 
            print("Send: %r" % message)
            writer.write(message)
            await writer.drain()

        print("Close the client socket")
        writer.close()

    def launch_server(self):
        self.loop = asyncio.get_event_loop()
        self.coro = asyncio.start_server(self.handle_echo, '127.0.0.1', 7780, loop=self.loop)
        self.server = self.loop.run_until_complete(self.coro)

        # Serve requests until Ctrl+C is pressed
        print('Serving on {}'.format(self.server.sockets[0].getsockname()))
        try:
            self.loop.run_forever()
        except KeyboardInterrupt:
            pass
        finally:
            # Close the server
            self.server.close()
            self.loop.run_until_complete(self.server.wait_closed())
            self.loop.close()

def main():
    server = responder()
    server.launch_server()

if __name__ == '__main__':
    main()

The code generates the number stream but it runs through the entire list before moving on. Further I never get a value back.

My client code (which never gets anything back)

import asyncio


async def capture_stream(reader):

    while not reader.at_eof:
        data = await reader.read(100)
        print( f'{who} received {len(data)} bytes' )


async def tcp_echo_client(message, loop):
    reader, writer = await asyncio.open_connection('127.0.0.1',7780,loop=loop)
    
    print('Send: %r' % message)
    writer.write(message.encode())

    if (message == "START_RUN"):
        data = await reader.read(100)
        print('Received: %r' % data.decode())
    else:
        collect_data = asyncio.create_task(capture_stream)
        data = await collect_data


    print('Close the socket')
    writer.close()
    
message = 'START_RUN'
loop = asyncio.get_event_loop()
loop.run_until_complete(tcp_echo_client(message, loop))
loop.close()
TheCodeNovice
  • 750
  • 14
  • 35
  • Not sure about the server code, but `asyncio.create_task(capture_stream)` doesn't look correct - `capture_stream` is a coroutine that you need to _call_ before passing it to `asyncio.create_task()`. Also, since you're awaiting the task immediately, there is no point in creating the task, you can just `await capture_stream(reader)`. – user4815162342 Apr 20 '21 at 08:15
  • There are multiple other issues with the client code. `reader.at_eof` is a method that should be called. The code sends the non-self-delimited `START_RUN` message over a TCP connection without any framing. `capture_stream` references the non-existent global variable `who`. The `tcp_echo_client` doesn't do echoing. – user4815162342 Apr 20 '21 at 08:22
  • @user4815162342 Thanks for the comments, I am very new to asyncio. I was under the impression that if I have a loop running already I need to inject activities into the loop doing tasks. In the examples I have looked at they seem to put a task wrapper around the coroutine and await it. I think at least. Where I am confused is how I order the sequencing. I want queue to be process and sending data out as the queue fills and still have a GUI that can respond to user input. Namely to stop the stream, I wont ever have an EOF and eventually have to work in a way of stopping the data flow as well. – TheCodeNovice Apr 20 '21 at 13:38

0 Answers0