Skip to content

Commit 2678b9b

Browse files
Improve handling of situation when stdin is not a terminal.
Print warning and continue instead of failing with an assertion.
1 parent c2c419f commit 2678b9b

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

prompt_toolkit/application/application.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ def __init__(self, layout=None,
240240
self.renderer = Renderer(
241241
self._merged_style,
242242
self.output,
243+
self.input,
243244
full_screen=full_screen,
244245
mouse_support=mouse_support,
245246
cpr_not_supported_callback=self.cpr_not_supported_callback)

prompt_toolkit/input/vt100.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ class Vt100Input(Input):
2323
Vt100 input for Posix systems.
2424
(This uses a posix file descriptor that can be registered in the event loop.)
2525
"""
26-
def __init__(self, stdin):
27-
# The input object should be a TTY.
28-
assert stdin.isatty()
26+
_fds_not_a_terminal = set() # For the error messages. Only display "Input
27+
# is not a terminal" once per file descriptor.
2928

29+
def __init__(self, stdin):
3030
# Test whether the given input object has a file descriptor.
3131
# (Idle reports stdin to be a TTY, but fileno() is not implemented.)
3232
try:
@@ -39,6 +39,20 @@ def __init__(self, stdin):
3939
else:
4040
raise io.UnsupportedOperation('Stdin is not a terminal.')
4141

42+
# Even when we have a file descriptor, it doesn't mean it's a TTY.
43+
# Normally, this requires a real TTY device, but people instantiate
44+
# this class often during unit tests as well. They use for instance
45+
# pexpect to pipe data into an application. For convenience, we print
46+
# an error message and go on.
47+
isatty = stdin.isatty()
48+
fd = stdin.fileno()
49+
50+
if not isatty and fd not in Vt100Input._fds_not_a_terminal:
51+
msg = 'Warning: Input is not to a terminal (fd=%r).\n'
52+
sys.stderr.write(msg % fd)
53+
Vt100Input._fds_not_a_terminal.add(fd)
54+
55+
#
4256
self.stdin = stdin
4357

4458
# Create a backup of the fileno(). We want this to work even if the
@@ -52,7 +66,9 @@ def __init__(self, stdin):
5266

5367
@property
5468
def responds_to_cpr(self):
55-
return True
69+
# When the input is a tty, we assume that CPR is supported.
70+
# It's not when the input is piped from Pexpect.
71+
return self.stdin.isatty()
5672

5773
def attach(self, input_ready_callback):
5874
"""

prompt_toolkit/renderer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from prompt_toolkit.eventloop import Future, From, ensure_future, get_event_loop
88
from prompt_toolkit.filters import to_filter
99
from prompt_toolkit.formatted_text import to_formatted_text
10+
from prompt_toolkit.input.base import Input
1011
from prompt_toolkit.layout.mouse_handlers import MouseHandlers
1112
from prompt_toolkit.layout.screen import Point, Screen, WritePosition
1213
from prompt_toolkit.output import Output, ColorDepth
@@ -265,13 +266,17 @@ class Renderer(object):
265266
"""
266267
CPR_TIMEOUT = 2 # Time to wait until we consider CPR to be not supported.
267268

268-
def __init__(self, style, output, full_screen=False, mouse_support=False, cpr_not_supported_callback=None):
269+
def __init__(self, style, output, input, full_screen=False,
270+
mouse_support=False, cpr_not_supported_callback=None):
271+
269272
assert isinstance(style, BaseStyle)
270273
assert isinstance(output, Output)
274+
assert isinstance(input, Input)
271275
assert callable(cpr_not_supported_callback) or cpr_not_supported_callback is None
272276

273277
self.style = style
274278
self.output = output
279+
self.input = input
275280
self.full_screen = full_screen
276281
self.mouse_support = to_filter(mouse_support)
277282
self.cpr_not_supported_callback = cpr_not_supported_callback
@@ -283,6 +288,8 @@ def __init__(self, style, output, full_screen=False, mouse_support=False, cpr_no
283288
# Future set when we are waiting for a CPR flag.
284289
self._waiting_for_cpr_futures = deque()
285290
self.cpr_support = CPR_Support.UNKNOWN
291+
if not input.responds_to_cpr:
292+
self.cpr_support = CPR_Support.NOT_SUPPORTED
286293

287294
# Cache for the style.
288295
self._attrs_for_style = None

0 commit comments

Comments
 (0)