Skip to content

Commit 80efdf6

Browse files
kieranyyudavidism
authored andcommitted
Raise an exception on end of input in CliRunner
1 parent cfa6f4a commit 80efdf6

File tree

5 files changed

+27
-6
lines changed

5 files changed

+27
-6
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Version 8.2.1
77
:issue:`2897` :pr:`2930`
88
- Fix shell completion for nested groups. :issue:`2906` :pr:`2907`
99
- Flush ``sys.stderr`` at the end of ``CliRunner.invoke``. :issue:`2682`
10+
- Fix EOF handling for stdin input in CliRunner. :issue:`2787`
1011

1112
Version 8.2.0
1213
-------------

src/click/testing.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ def name(self) -> str:
116116
def mode(self) -> str:
117117
return self._mode
118118

119+
def __next__(self) -> str: # type: ignore
120+
try:
121+
line = super().__next__()
122+
except StopIteration as e:
123+
raise EOFError() from e
124+
return line
125+
119126

120127
def make_input_stream(
121128
input: str | bytes | t.IO[t.Any] | None, charset: str
@@ -341,7 +348,7 @@ def isolation(
341348
@_pause_echo(echo_input) # type: ignore
342349
def visible_input(prompt: str | None = None) -> str:
343350
sys.stdout.write(prompt or "")
344-
val = text_input.readline().rstrip("\r\n")
351+
val = next(text_input).rstrip("\r\n")
345352
sys.stdout.write(f"{val}\n")
346353
sys.stdout.flush()
347354
return val
@@ -350,7 +357,7 @@ def visible_input(prompt: str | None = None) -> str:
350357
def hidden_input(prompt: str | None = None) -> str:
351358
sys.stdout.write(f"{prompt or ''}\n")
352359
sys.stdout.flush()
353-
return text_input.readline().rstrip("\r\n")
360+
return next(text_input).rstrip("\r\n")
354361

355362
@_pause_echo(echo_input) # type: ignore
356363
def _getchar(echo: bool) -> str:

tests/test_chain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ def processor(iterator):
163163
return processor
164164

165165
result = runner.invoke(cli, args, input=input)
166-
assert not result.exception
167-
assert result.output.splitlines() == expect
166+
# last two lines are '' and 'Aborted!'
167+
assert result.output.splitlines()[:-2] == expect
168168

169169

170170
def test_args_and_chain(runner):

tests/test_termui.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def test_file_prompt_default_format(runner, file_kwargs):
242242
def cli(f):
243243
click.echo(f.name)
244244

245-
result = runner.invoke(cli)
245+
result = runner.invoke(cli, input="\n")
246246
assert result.output == f"file [{__file__}]: \n{__file__}\n"
247247

248248

@@ -451,7 +451,7 @@ def cli(value, o):
451451
[
452452
(True, "password\npassword", None, "password"),
453453
("Confirm Password", "password\npassword\n", None, "password"),
454-
(True, "", "", ""),
454+
(True, "\n\n", "", ""),
455455
(False, None, None, None),
456456
],
457457
)

tests/test_utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,19 @@ def f(_):
179179
assert out == "Password:\ninterrupted\n"
180180

181181

182+
def test_prompts_eof(runner):
183+
"""If too few lines of input are given, prompt should exit, not hang."""
184+
185+
@click.command
186+
def echo():
187+
for _ in range(3):
188+
click.echo(click.prompt("", type=int))
189+
190+
# only provide two lines of input for three prompts
191+
result = runner.invoke(echo, input="1\n2\n")
192+
assert result.exit_code == 1
193+
194+
182195
def _test_gen_func():
183196
yield "a"
184197
yield "b"

0 commit comments

Comments
 (0)