Skip to content

Commit cd2b153

Browse files
Handle I/O operation on closed file.
Bugfix for ptpython/Xonsh. This handles the situation where `sys.stdin` gets closed, by running for instance `sys.stdin.close()`.
1 parent 0f69d32 commit cd2b153

File tree

1 file changed

+34
-7
lines changed

1 file changed

+34
-7
lines changed

Diff for: prompt_toolkit/input/vt100.py

+34-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
)
1919

2020
from ..key_binding import KeyPress
21+
from ..utils import DummyContext
2122
from .base import Input
2223
from .posix_utils import PosixStdinReader
2324
from .vt100_parser import Vt100Parser
@@ -44,7 +45,9 @@ def __init__(self, stdin: TextIO) -> None:
4445
# (Idle reports stdin to be a TTY, but fileno() is not implemented.)
4546
try:
4647
# This should not raise, but can return 0.
47-
stdin.fileno()
48+
fileno = stdin.fileno()
49+
except ValueError:
50+
raise EOFError # stdin closed?
4851
except io.UnsupportedOperation as e:
4952
if "idlelib.run" in sys.modules:
5053
raise io.UnsupportedOperation(
@@ -72,10 +75,10 @@ def __init__(self, stdin: TextIO) -> None:
7275

7376
# Create a backup of the fileno(). We want this to work even if the
7477
# underlying file is closed, so that `typeahead_hash()` keeps working.
75-
self._fileno = stdin.fileno()
78+
self._fileno = fileno
7679

7780
self._buffer: List[KeyPress] = [] # Buffer to collect the Key objects.
78-
self.stdin_reader = PosixStdinReader(self._fileno, encoding=stdin.encoding)
81+
self.stdin_reader = PosixStdinReader(fileno, encoding=stdin.encoding)
7982
self.vt100_parser = Vt100Parser(
8083
lambda key_press: self._buffer.append(key_press)
8184
)
@@ -123,16 +126,40 @@ def flush_keys(self) -> List[KeyPress]:
123126

124127
@property
125128
def closed(self) -> bool:
126-
return self.stdin_reader.closed
129+
# See: https://github.com/prompt-toolkit/ptpython/issues/463
130+
# For ptpython, if the user does `sys.stdin.close()`, then
131+
# `stdin_reader` will not yet be closed, but `stdin` will be closed.
132+
# After this, `stdin.fileno()` and `stdin.read()` will both raise
133+
# `ValueError`.
134+
return self.stdin.closed or self.stdin_reader.closed
127135

128136
def raw_mode(self) -> ContextManager[None]:
129-
return raw_mode(self.stdin.fileno())
137+
try:
138+
fileno = self.stdin.fileno()
139+
except ValueError:
140+
# Stdin closed?
141+
return DummyContext()
142+
143+
return raw_mode(fileno)
130144

131145
def cooked_mode(self) -> ContextManager[None]:
132-
return cooked_mode(self.stdin.fileno())
146+
try:
147+
fileno = self.stdin.fileno()
148+
except ValueError:
149+
# Stdin closed?
150+
return DummyContext()
151+
152+
return cooked_mode(fileno)
133153

134154
def fileno(self) -> int:
135-
return self.stdin.fileno()
155+
"""
156+
Return file descriptor.
157+
Raises `EOFError` when the input is closed.
158+
"""
159+
try:
160+
return self.stdin.fileno()
161+
except ValueError:
162+
raise EOFError
136163

137164
def typeahead_hash(self) -> str:
138165
return "fd-%s" % (self._fileno,)

0 commit comments

Comments
 (0)