Skip to content

Commit 484930c

Browse files
committed
Removed need for constants.EOF.
1 parent df1db8a commit 484930c

File tree

3 files changed

+51
-49
lines changed

3 files changed

+51
-49
lines changed

cmd2/cmd2.py

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def __init__(
433433
self.self_in_py = False
434434

435435
# Commands to exclude from the help menu and completion
436-
self.hidden_commands = [constants.EOF, '_relative_run_script']
436+
self.hidden_commands = ['_eof', '_relative_run_script']
437437

438438
# Initialize history from a persistent history file (if present)
439439
self.persistent_history_file = ''
@@ -453,7 +453,7 @@ def __init__(
453453
self.session = self._init_session()
454454

455455
# Commands to exclude from the history command
456-
self.exclude_from_history = [constants.EOF, 'history']
456+
self.exclude_from_history = ['_eof', 'history']
457457

458458
# Dictionary of macro names and their values
459459
self.macros: dict[str, Macro] = {}
@@ -2821,11 +2821,6 @@ def runcmds_plus_hooks(
28212821
def _complete_statement(self, line: str) -> Statement:
28222822
"""Keep accepting lines of input until the command is complete.
28232823
2824-
There is some pretty hacky code here to handle some quirks of
2825-
self._read_command_line(). It returns a literal 'eof' if the input
2826-
pipe runs out. We can't refactor it because we need to retain
2827-
backwards compatibility with the standard library version of cmd.
2828-
28292824
:param line: the line being parsed
28302825
:return: the completed Statement
28312826
:raises Cmd2ShlexError: if a shlex error occurs (e.g. No closing quotation)
@@ -2859,12 +2854,10 @@ def _complete_statement(self, line: str) -> Statement:
28592854
self._multiline_in_progress = line + '\n'
28602855

28612856
# Get next line of this command
2862-
nextline = self._read_command_line(self.continuation_prompt)
2863-
if nextline == constants.EOF:
2864-
# they entered either a blank line, or we hit an EOF
2865-
# for some other reason. Turn the literal 'eof'
2866-
# into a blank line, which serves as a command
2867-
# terminator
2857+
try:
2858+
nextline = self._read_command_line(self.continuation_prompt)
2859+
except EOFError:
2860+
# Add a blank line, which serves as a command terminator.
28682861
nextline = '\n'
28692862
self.poutput(nextline)
28702863

@@ -3332,26 +3325,25 @@ def _read_command_line(self, prompt: str) -> str:
33323325
"""Read the next command line from the input stream.
33333326
33343327
:param prompt: prompt to display to user
3335-
:return: command line text or 'eof' if an EOFError was caught
3336-
:raises Exception: any exceptions raised by prompt(), except EOFError
3328+
:return: the line read from stdin with all trailing new lines removed
3329+
:raises EOFError: if the input stream is closed or the user signals EOF (e.g., Ctrl+D)
3330+
:raises Exception: any other exceptions raised by prompt()
33373331
"""
3338-
try:
3339-
# Use dynamic prompt if the prompt matches self.prompt
3340-
def get_prompt() -> ANSI | str:
3341-
return ANSI(self.prompt)
3342-
3343-
prompt_to_use: Callable[[], ANSI | str] | ANSI | str = ANSI(prompt)
3344-
if prompt == self.prompt:
3345-
prompt_to_use = get_prompt
3346-
3347-
return self._read_raw_input(
3348-
prompt=prompt_to_use,
3349-
session=self.session,
3350-
completer=self.completer,
3351-
pre_run=self.pre_prompt,
3352-
)
3353-
except EOFError:
3354-
return constants.EOF
3332+
3333+
# Use dynamic prompt if the prompt matches self.prompt
3334+
def get_prompt() -> ANSI | str:
3335+
return ANSI(self.prompt)
3336+
3337+
prompt_to_use: Callable[[], ANSI | str] | ANSI | str = ANSI(prompt)
3338+
if prompt == self.prompt:
3339+
prompt_to_use = get_prompt
3340+
3341+
return self._read_raw_input(
3342+
prompt=prompt_to_use,
3343+
session=self.session,
3344+
completer=self.completer,
3345+
pre_run=self.pre_prompt,
3346+
)
33553347

33563348
def _cmdloop(self) -> None:
33573349
"""Repeatedly issue a prompt, accept input, parse it, and dispatch to apporpriate commands.
@@ -3373,6 +3365,8 @@ def _cmdloop(self) -> None:
33733365
except KeyboardInterrupt:
33743366
self.poutput('^C')
33753367
line = ''
3368+
except EOFError:
3369+
line = "_eof"
33763370

33773371
# Run the command along with all associated pre and post hooks
33783372
stop = self.onecmd_plus_hooks(line)
@@ -4193,17 +4187,17 @@ def do_shortcuts(self, _: argparse.Namespace) -> None:
41934187
self.last_result = True
41944188

41954189
@staticmethod
4196-
def _build_eof_parser() -> Cmd2ArgumentParser:
4197-
eof_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Called when Ctrl-D is pressed.")
4198-
eof_parser.epilog = eof_parser.create_text_group(
4190+
def _build__eof_parser() -> Cmd2ArgumentParser:
4191+
_eof_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Called when Ctrl-D is pressed.")
4192+
_eof_parser.epilog = _eof_parser.create_text_group(
41994193
"Note",
42004194
"This command is for internal use and is not intended to be called from the command line.",
42014195
)
42024196

4203-
return eof_parser
4197+
return _eof_parser
42044198

4205-
@with_argparser(_build_eof_parser)
4206-
def do_eof(self, _: argparse.Namespace) -> bool | None:
4199+
@with_argparser(_build__eof_parser)
4200+
def do__eof(self, _: argparse.Namespace) -> bool | None:
42074201
"""Quit with no arguments, called when Ctrl-D is pressed.
42084202
42094203
This can be overridden if quit should be called differently.

cmd2/constants.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# nothing here should be considered part of the public API of this module
55

66
INFINITY = float('inf')
7-
EOF = 'eof'
87

98
# Used for command parsing, output redirection, completion, and word breaks. Do not change.
109
QUOTES = ['"', "'"]

tests/test_cmd2.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,14 +1181,14 @@ def say_app():
11811181

11821182
def test_ctrl_c_at_prompt(say_app, monkeypatch) -> None:
11831183
read_command_mock = mock.MagicMock(name='_read_command_line')
1184-
read_command_mock.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', constants.EOF]
1184+
read_command_mock.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'quit']
11851185
monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock)
11861186

11871187
say_app.cmdloop()
11881188

11891189
# And verify the expected output to stdout
11901190
out = say_app.stdout.getvalue()
1191-
assert out == 'hello\n^C\ngoodbye\n\n'
1191+
assert out == 'hello\n^C\ngoodbye\n'
11921192

11931193

11941194
class ShellApp(cmd2.Cmd):
@@ -1877,14 +1877,13 @@ def test_is_text_file_bad_input(base_app) -> None:
18771877
utils.is_text_file('.')
18781878

18791879

1880-
def test_eof(base_app) -> None:
1881-
# Only thing to verify is that it returns True
1882-
assert base_app.do_eof('')
1883-
assert base_app.last_result is True
1880+
def test__eof(base_app) -> None:
1881+
base_app.do_quit = mock.MagicMock(return_value=True)
1882+
assert base_app.do__eof('')
1883+
base_app.do_quit.assert_called_once_with('')
18841884

18851885

18861886
def test_quit(base_app) -> None:
1887-
# Only thing to verify is that it returns True
18881887
assert base_app.do_quit('')
18891888
assert base_app.last_result is True
18901889

@@ -2063,11 +2062,21 @@ def test_custom_stdout() -> None:
20632062

20642063

20652064
def test_read_command_line_eof(base_app, monkeypatch) -> None:
2065+
"""Test that _read_command_line passes up EOFErrors."""
2066+
read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError)
2067+
monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock)
2068+
2069+
with pytest.raises(EOFError):
2070+
base_app._read_command_line("Prompt> ")
2071+
2072+
2073+
def test_read_input_eof(base_app, monkeypatch) -> None:
2074+
"""Test that read_input passes up EOFErrors."""
20662075
read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError)
20672076
monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock)
20682077

2069-
line = base_app._read_command_line("Prompt> ")
2070-
assert line == constants.EOF
2078+
with pytest.raises(EOFError):
2079+
base_app.read_input("Prompt> ")
20712080

20722081

20732082
def test_poutput_string(outsim_app) -> None:
@@ -3002,10 +3011,10 @@ def test_get_all_commands(base_app) -> None:
30023011
# Verify that the base app has the expected commands
30033012
commands = base_app.get_all_commands()
30043013
expected_commands = [
3014+
'_eof',
30053015
'_relative_run_script',
30063016
'alias',
30073017
'edit',
3008-
constants.EOF,
30093018
'help',
30103019
'history',
30113020
'ipy',

0 commit comments

Comments
 (0)