MagicStack / MagicStack/uvloop
Asynchronously gathering user input seems to break in uvloop but not in default asyncio loop
還沒有人認領這個 Issue。
- 主要語言
- Cython
- 星號
- 11.9k
- 分支
- 616
- PR 合併指標
- 30 天內沒有已合併 PR
描述
- uvloop version: 0.17.0, 0.16.0, 0.15.2
- Python version: 3.9, 3.11
- Platform: Lenovo T15g Gen 1, Ubuntu 20.04, Kernel 5.15.0-46-generic
- Can you reproduce the bug with
PYTHONASYNCIODEBUGin env?: Yes, though doesn't look like it provides any useful info - Does uvloop behave differently from vanilla asyncio? How?: Asynchronously gathering user input from the terminal doesn't seem to be possible with any method that I am aware of. When not using uvloop, there are two approaches that work fine.
Here is the minimal reproducible example that I am using to try to understand this bug (note requires installing uvloop and :
import os
os.environ['PYTHONASYNCIODEBUG'] = '1'
import asyncio
from typing import Tuple, Type, Optional
from types import TracebackType
import traceback
import sys
import uvloop
import aioconsole
async def connect_stdin_stdout() -> Tuple[asyncio.StreamReader, asyncio.StreamWriter]:
"""Returns asyncio-compatible stream readers and writers for stdin and stdout.
Useful for CLI interaction without blocking the asyncio event loop.
"""
loop = asyncio.get_event_loop()
reader = asyncio.StreamReader()
r_protocol = asyncio.StreamReaderProtocol(reader)
await loop.connect_read_pipe(lambda: r_protocol, sys.stdin)
w_transport, w_protocol = await loop.connect_write_pipe(
asyncio.streams.FlowControlMixin, sys.stdout
)
writer = asyncio.StreamWriter(w_transport, w_protocol, reader, loop)
return reader, writer
def _log_exceptions(
exc_type: Type[BaseException],
exc_value: BaseException,
exc_traceback: Optional[TracebackType]
):
traceback_lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
print("---- CUSTOM ERROR REPORT ----")
for line in traceback_lines:
print(line)
print("-----------------------------")
async def main(trigger_exception = False, use_aioconsole = False) -> None:
if use_aioconsole:
stdout = sys.stdout.buffer
user_input = (await aioconsole.ainput(">> ")).encode()
else:
stdin, stdout = await connect_stdin_stdout()
stdout.write(b">> ")
user_input = (await stdin.readline())
if trigger_exception:
raise RuntimeError()
stdout.write(user_input)
if __name__ == "__main__":
# Test 1
# asyncio.run(main())
# Test 2
# uvloop.install()
# asyncio.run(main())
# Test 3
# asyncio.run(main(use_aioconsole=True))
# Test 4
# uvloop.install()
# asyncio.run(main(use_aioconsole=True))
# Test 5
# sys.excepthook = _log_exceptions
# asyncio.run(main(trigger_exception=True, use_aioconsole=True))
# Test 6
# uvloop.install()
# sys.excepthook = _log_exceptions
# asyncio.run(main(trigger_exception=True, use_aioconsole=True))
There are two main things that I would like to be able to do, that I cannot find a solution for:
- Gather user input from the terminal asynchronously without using another thread
- Ensure we can still make some pretty error logs when exceptions occur
Both of these tasks work fine with the default asyncio loop, but seem to fail for me with uvloop. The two approaches I know are connect_stdin_stdout from this stackoverflow answer, and to use a third party library aioconsole (which under the hood does a lot of the same things as the stackoverflow answer).
Are you able to replicate on other systems? It is easy to replicate for me on a variety of python and uvloop installations (noted above).
Is there a workaround I could use to gather user input in a uvloop environment?
In case replication is hard, here are the outputs for each test on my system
- Test 1
>> a
a
- Test 2 (note it just hangs forever and never returns)
>> a
- Test 3
>> a
a⏎
- Test 4
>> a
a⏎
- Test 5 (note the "custom error report" formatting)
>> a
---- CUSTOM ERROR REPORT ----
Traceback (most recent call last):
File "/home/ethan/mariana/src/python/potomac/node/test_node.py", line 74, in <module>
asyncio.run(main(trigger_exception=True, use_aioconsole=True))
File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/home/ethan/mariana/src/python/potomac/node/test_node.py", line 53, in main
raise RuntimeError()
RuntimeError
-----------------------------
- Test 6 (apologies that this is totally useless, I think something is getting swallowed but I'm not sure why)
>> a
Error in sys.excepthook:
Original exception was:
貢獻指南
這個儲存庫沒有索引到貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
從最小重現開始,尤其是 connect_stdin_stdout()、asyncio.run()、uvloop.install() 和 aioconsole 路徑。在報告中的 Python、uvloop 和 Ubuntu 版本上執行列出的六個測試,然後將 uvloop 與預設的 asyncio 迴圈進行比較。當終端輸入能夠非同步運作,且自訂例外報告的行為一致,不會掛起或吞掉錯誤時,即表示完成。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- cli
- Issue 類型
- 缺陷
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 活躍度
- 停滯
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100