`EOFError` when using `input()` after `ainput()`
anhtuan23 opened this issue · 2 comments
Hi, thanks for this very helpful package.
Running this simple script:
import asyncio
from aioconsole.stream import ainput
async def main():
input1 = await ainput("Input1: ")
input2 = input("Input2: ")
if __name__ == "__main__":
asyncio.run(main())
I have the following error appears after the first input:
% python draft.py
Input1: hello
Input2: Traceback (most recent call last):
File "/Users/kaestrl/draft.py", line 10, in <module>
asyncio.run(main())
File "/Users/kaestrl/miniforge3/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/Users/kaestrl/miniforge3/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/Users/kaestrl/draft.py", line 7, in main
input2 = input("Input2: ")
EOFError
This problem appears on MacOS Monterey 12.1's terminal, but not on Windows 11.
Hi @anhtuan23 and thanks for the report !
The problem here is that the stdin file descriptor needs to be set to non-blocking mode in order to be registered in the asyncio event loop, but the input
builtin requires this file descriptor to be in blocking mode. If you were to patch the input()
builtin with the following code, your example would work properly:
import os
import sys
import builtins
def input(arg):
try:
os.set_blocking(sys.stdin.fileno(), True)
return builtins.input(arg)
finally:
os.set_blocking(sys.stdin.fileno(), False)
However, I would advise to simply avoid using input()
from an asyncio coroutine as it would block the event loop while waiting for the user input.
I hope that helps :)
Thank you very much @vxgmichel, your code works beautifully.
I have to use blocking input()
because my script have an outer "menu" loop that also continuously waiting for input.
It's something like this:
import asyncio
from aioconsole.stream import ainput
import os
import sys
import builtins
def _binput(arg):
try:
os.set_blocking(sys.stdin.fileno(), True)
return builtins.input(arg)
finally:
os.set_blocking(sys.stdin.fileno(), False)
async def _job1():
answer = _binput("Answer: ")
# answer = await ainput("Answer: ")
print(f"You answered {answer}")
while True:
print("Running job1...")
await asyncio.sleep(1)
async def main():
running_task = None
while True:
_input = await ainput("Enter job name: ")
print(f"You entered {_input}")
if _input == "job1":
running_task = asyncio.create_task(_job1())
if _input == "stop":
try:
running_task.cancel()
except Exception as e:
print(f"Cannot cancel task: {e}")
if _input == "exit":
break
if __name__ == "__main__":
asyncio.run(main())
If ainput()
is used in _job1()
, answer
would never receive user input since the "menu" outer loop would consume it first.
Thanks again for your reply.