diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 285841ca5e5b1c..0b8f1686a568b1 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -435,9 +435,14 @@ def do(self) -> None: class help(Command): def do(self) -> None: import _sitebuiltins - - with self.reader.suspend(): - self.reader.msg = _sitebuiltins._Helper()() # type: ignore[assignment, call-arg] + if self.reader.help_mode: + self.reader.help_mode = False + raise KeyboardInterrupt + else: + self.reader.help_mode = True + with self.reader.suspend(): + self.reader.msg = _sitebuiltins._Helper()() # type: ignore[assignment, call-arg] + self.reader.help_mode = False class invalid_key(Command): diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 4b0700d069c621..859211acebb354 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -234,6 +234,7 @@ class Reader: dirty: bool = False finished: bool = False paste_mode: bool = False + help_mode: bool = False in_bracketed_paste: bool = False commands: dict[str, type[Command]] = field(default_factory=make_default_commands) last_command: type[Command] | None = None @@ -665,7 +666,7 @@ def suspend(self) -> SimpleContextManager: self.restore() yield finally: - for arg in ("msg", "ps1", "ps2", "ps3", "ps4", "paste_mode"): + for arg in ("msg", "ps1", "ps2", "ps3", "ps4", "paste_mode", "help_mode"): setattr(self, arg, prev_state[arg]) self.prepare() diff --git a/Lib/test/test_pyrepl/support.py b/Lib/test/test_pyrepl/support.py index 45e3bf758f17de..f3a4032e4bc851 100644 --- a/Lib/test/test_pyrepl/support.py +++ b/Lib/test/test_pyrepl/support.py @@ -58,6 +58,7 @@ def prepare_reader(console: Console, **kwargs): reader = ReadlineAlikeReader(console=console, config=config) reader.more_lines = partial(more_lines, namespace=None) reader.paste_mode = True # Avoid extra indents + reader.help_mode = False def get_prompt(lineno, cursor_on_line) -> str: return "" diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index 6c72a1d39c55df..03147bab698ce0 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -175,6 +175,20 @@ def test_up_arrow_after_ctrl_r(self): reader, _ = handle_all_events(events) self.assert_screen_equals(reader, "") + def test_help_toggles_instead_of_nesting(self): + + events = [ + Event(evt="key", data="f1", raw=bytearray(b"")), + ] + + no_paste_reader = functools.partial( + prepare_reader, + paste_mode=False, + help_mode=True, + ) + reader, _ = handle_all_events(events, prepare_reader=no_paste_reader) + self.assertFalse(reader.help_mode) + def test_newline_within_block_trailing_whitespace(self): # fmt: off code = ( diff --git a/Misc/NEWS.d/next/Library/2025-01-21-19-46-20.gh-issue-121584.O3lEdm.rst b/Misc/NEWS.d/next/Library/2025-01-21-19-46-20.gh-issue-121584.O3lEdm.rst new file mode 100644 index 00000000000000..99a138abf93175 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-21-19-46-20.gh-issue-121584.O3lEdm.rst @@ -0,0 +1,2 @@ +Toggle helper instead of nesting instances when using key bindings in the +new REPL.