0

I am coding a command line/shell chat. To better handle input and ouput I use asyncio. But asyncio doesn't work with python's standard input() function. So I have been looking for other solutions and came across aioconsole. But the documentation is quite hard to understand (and imho not that well written.) So could you please help me implement an asyncronous input in the function message_sender() by replacing the current placeholder function called asynchronous_input(). (A function without aioconsole would also work, it's just what I came across) And yes, there are other SO posts about similar things, but they all use very old asynio versions.

import asyncio
import struct

async def chat_client():
    reader, writer = await asyncio.open_connection("xxx.xxx.xxx.xxx", xxx)
    await message_sender(writer)
    await message_reciever(reader)

async def message_sender(writer):
    try:
        while True:
            message = await #asynchronous_input()
            await message_writer(message, writer)
    except asyncio.TimeoutError: # Just ignore this exception handler for now, it's only there for later
        print("----------- You lost your connection -----------")
        writer.close()
        await writer.wait_closed()
        quit()

async def message_reciever(reader):
        while True:
            size, = struct.unpack('<L', await reader.readexactly(4)) 
            rcv_message = await reader.readexactly(size)
            print(rcv_message.decode())

async def message_writer(message, writer):
        data = message.encode()
        writer.write(struct.pack('<L', len(data)))
        writer.write(data)
        await writer.drain()

try:
    message = ""
    asyncio.run(chat_client())
except KeyboardInterrupt:
    print("----------- You left the Chat. -----------")
    quit()
VPfB
  • 14,927
  • 6
  • 41
  • 75
SoccerFan
  • 55
  • 6

1 Answers1

1

If you want to use asyncio only:

Make this preparation once, it is quite low-level (set LIMIT e.g. to 8192 (8K buffer)):

loop = asyncio.get_running_loop()
rstream = asyncio.StreamReader(limit=LIMIT, loop=loop)
protocol = asyncio.StreamReaderProtocol(rstream, loop=loop)
await loop.connect_read_pipe(lambda: protocol, sys.stdin)

and then, when you want to read a line asynchronously:

line = (await rstream.readline()).decode()

You'll get a text line with the newline included or an empty string on EOF.

However, it was only yesterday I posted a related question that it has really very limitied editing features. You might want to read it, a specialized library was recommended there: How to have a comfortable (e.g. GNU-readline style) input line in an asyncio task?

VPfB
  • 14,927
  • 6
  • 41
  • 75
  • Thank for your time, but, after implementing your suggestion, it never actually calls `message_reciever()`, but gets stuck in `message_seder()` – SoccerFan Dec 02 '21 at 11:58
  • Or to be more precise, whichever function I call first so `message_sender()`or `message_reciever` the other never runs – SoccerFan Dec 02 '21 at 12:20
  • @SoccerFan While one function waits in `await`, other asyncio tasks can run in that time. But you don't have other tasks. If you want to run the sender and the receiver independent of each other, you have to create tasks. There is an example how to run multiple tasks concurrently: https://docs.python.org/3/library/asyncio-task.html#running-tasks-concurrently – VPfB Dec 02 '21 at 12:34
  • Oh yeah, thank you for the reminder, I confused some terminology – SoccerFan Dec 02 '21 at 12:36
  • @SoccerFan I'm glad I could help. – VPfB Dec 02 '21 at 12:56