Python 异步:在做其他事情时等待 stdin 输入

Tee*_*kin 11 python-3.x python-asyncio

我正在尝试创建一个 WebSocket 命令行客户端,它等待来自 WebSocket 服务器的消息但同时等待用户输入。

每秒定期轮询多个在线资源在服务器上运行良好,(localhost:6789在本示例中运行的那个),但sleep()它使用而不是使用 Python 的正常方法asyncio.sleep(),这是有道理的,因为睡眠和异步睡眠不是一回事,在至少不是在引擎盖下。

同样,等待用户输入和异步等待用户输入不是一回事,但我无法弄清楚如何以异步等待任意秒数的方式异步等待用户输入,所以客户端可以处理来自 WebSocket 服务器的传入消息,同时等待用户输入。

希望的else-clause下面的评论monitor_cmd()解释了我的意思:

import asyncio
import json
import websockets

async def monitor_ws():
    uri = 'ws://localhost:6789'
    async with websockets.connect(uri) as websocket:
        async for message in websocket:
            print(json.dumps(json.loads(message), indent=2, sort_keys=True))

async def monitor_cmd():
    while True:

        sleep_instead = False

        if sleep_instead:
            await asyncio.sleep(1)
            print('Sleeping works fine.')
        else:
            # Seems like I need the equivalent of:
            # line = await asyncio.input('Is this your line? ')
            line = input('Is this your line? ')
            print(line)
try:
    asyncio.get_event_loop().run_until_complete(asyncio.wait([
        monitor_ws(),
        monitor_cmd()
    ]))
except KeyboardInterrupt:
    quit()
Run Code Online (Sandbox Code Playgroud)

这段代码只是无限期地等待输入,同时什么都不做,我明白为什么。我不明白的是如何解决它。:)

当然,如果我以错误的方式思考这个问题,我也很乐意学习如何解决这个问题。

use*_*342 12

您可以使用aioconsole第三方包以异步友好的方式与 stdin 交互:

line = await aioconsole.ainput('Is this your line? ')
Run Code Online (Sandbox Code Playgroud)


mfu*_*man 9

aioconsole大量借用,如果您宁愿避免使用外部库,您可以定义自己的异步输入函数:

async def ainput(string: str) -> str:
    await asyncio.get_event_loop().run_in_executor(
            None, lambda s=string: sys.stdout.write(s+' '))
    return await asyncio.get_event_loop().run_in_executor(
            None, sys.stdin.readline)
Run Code Online (Sandbox Code Playgroud)


Lel*_*rth 5

Python 3.9引入了asyncio.to_thread,它可以用来简化mfurseman答案中的代码:

async def ainput(string: str) -> str:
    await asyncio.to_thread(sys.stdout.write, f'{string} ')
    return await asyncio.to_thread(sys.stdin.readline)
Run Code Online (Sandbox Code Playgroud)

请注意,sys.stdin.readline 返回换行符 '\n',而不input返回。如果您想ainput排除换行符,我建议进行以下更改:

async def ainput(string: str) -> str:
    await asyncio.to_thread(sys.stdout.write, f'{string} ')
    return (await asyncio.to_thread(sys.stdin.readline)).rstrip('\n')
Run Code Online (Sandbox Code Playgroud)