aioconsole
aioconsole copied to clipboard
Handle non-interactive scripts
Certain programs can call your python program without a stdin. For example when you use docker run
without the -i
option you can get the following exception when calling a script using create_standard_streams
:
Traceback (most recent call last):
File "/usr/lib/python3.8/asyncio/selector_events.py", line 261, in _add_reader
key = self._selector.get_key(fd)
File "/usr/lib/python3.8/selectors.py", line 192, in get_key
raise KeyError("{!r} is not registered".format(fileobj)) from None
KeyError: '0 is not registered'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/asyncio/events.py", line 81, in _run
self._context.run(self._callback, *self._args)
File "/usr/lib/python3.8/asyncio/selector_events.py", line 263, in _add_reader
self._selector.register(fd, selectors.EVENT_READ,
File "/usr/lib/python3.8/selectors.py", line 359, in register
self._selector.register(key.fd, poller_events)
PermissionError: [Errno 1] Operation not permitted
@AGhost-7 Thanks for the report !
Interesting, but how would you expect create_standard_streams
to behave in this case? Do you still need async access to stdout and stderr even though stdin is not available?
In that case I would probably expect to get None
for stdout and stderr. Its pretty common that applications will display things to the terminal differently based on whether or not a session is interactive, so it is useful to expose that information. Alternatively you could provide a noop shim object, but I think the better option is the former.
I've encountered the same issue. This exception could also occur when running the program in the background in a script.
Is there any idea how to detect whether stdin is readable? If so, it can actually be fixed outside this library by checking it first before create_standard_streams
.
I'm very tempted to use the following fix:
diff --git a/aioconsole/stream.py b/aioconsole/stream.py
index 29d9827..43fbbcb 100644
--- a/aioconsole/stream.py
+++ b/aioconsole/stream.py
@@ -5,6 +5,7 @@ import sys
import stat
import weakref
import asyncio
+import selectors
from collections import deque
from threading import Thread
from concurrent.futures import Future
@@ -25,6 +26,14 @@ def is_pipe_transport_compatible(pipe):
is_socket = stat.S_ISSOCK(mode)
if not (is_char or is_fifo or is_socket):
return False
+ # Fail early when the file descriptor cannot be registered.
+ # This happens with docker containers for instance.
+ # See issue #102: https://github.com/vxgmichel/aioconsole/issues/102
+ try:
+ with selectors.DefaultSelector() as selector:
+ selector.register(fileno, selectors.EVENT_READ | selectors.EVENT_WRITE)
+ except PermissionError:
+ return False
return True
This way, create_standard_streams
would return the fallback objects NonFileStreamReader/NonFileStreamWriter
.
Then you would get the following error when trying to await ainput
:
Traceback (most recent call last):
File "/usr/src/app/script.py", line 21, in <module>
asyncio.run(main())
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/usr/src/app/script.py", line 15, in main
print(await aioconsole.ainput())
File "/usr/src/app/aioconsole/stream.py", line 281, in ainput
raise EOFError
EOFError
This is very similar to the behavior with input
:
Traceback (most recent call last):
File "/usr/src/app/script.py", line 21, in <module>
asyncio.run(main())
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/usr/src/app/script.py", line 12, in main
print(input())
EOFError: EOF when reading a line
Note that stdout.write/stdout.drain
would also work as expected in the docker container.
What do you think @DCsunset @AGhost-7 ?
Yeah I think it's a good idea! I've also tested the code in your PR and it works without problem
Fixed in #106, available in v0.6.1 :tada: