Skip to content

Commit 7cd9ef3

Browse files
authored
PyREPL: Send PS1 as string (#26197)
Improve the integration of prompt handling in the REPL, ensuring that prompts are always passed as strings to `_pyrepl.simple_interact.multiline_input` * In `python_files/pythonrc.py`, patch `_pyrepl.simple_interact.multiline_input` so that the `ps1` and `ps2` prompt arguments are always converted to strings before being passed, preventing issues with non-string prompt objects (see python/cpython#158828) ### Testing * Adds a new test `test_pyrepl_multiline_input_converts_prompts_to_strings` in `python_files/tests/test_shell_integration.py` to verify that the patched `multiline_input` function correctly converts prompt arguments to strings before calling the original function.
1 parent a501428 commit 7cd9ef3

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

‎python_files/pythonrc.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,23 @@ def _initialize():
99
if sys.version_info >= (3, 13):
1010
import re
1111

12+
import _pyrepl.simple_interact
1213
import _pyrepl.utils
1314

1415
_pyrepl.utils.ANSI_ESCAPE_SEQUENCE = re.compile(
1516
r"\x1b(?:\[[ -@]*[A-~]|\][^\x1b\x07]*(?:\x07|\x1b\\))"
1617
)
1718

19+
# Patch _pyrepl.simple_interact.multiline_input to ensure prompts are strings
20+
_original_multiline_input = _pyrepl.simple_interact.multiline_input
21+
if getattr(_original_multiline_input, "_vscode_prompts_as_strings", False) is not True:
22+
23+
def _multiline_input(more_lines, ps1, ps2):
24+
return _original_multiline_input(more_lines, _str(ps1), _str(ps2))
25+
26+
_multiline_input.__dict__["_vscode_prompts_as_strings"] = True
27+
_pyrepl.simple_interact.multiline_input = _multiline_input
28+
1829
original_ps1 = ">>> "
1930
# PYTHONSTARTUP executes this file's code inside the user's __main__
2031
# namespace, so PS1.__str__'s globals are the user's globals. If the

‎python_files/tests/test_shell_integration.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,29 @@ def test_replacement_regex_preserves_csi_handling():
115115
)
116116

117117

118+
def test_pyrepl_multiline_input_converts_prompts_to_strings(monkeypatch):
119+
if sys.version_info < (3, 13):
120+
return
121+
122+
pyrepl_simple_interact = importlib.import_module("_pyrepl.simple_interact")
123+
original_multiline_input = Mock(return_value="statement")
124+
125+
with monkeypatch.context() as m:
126+
m.setattr(pyrepl_simple_interact, "multiline_input", original_multiline_input)
127+
importlib.reload(pythonrc)
128+
129+
more_lines = Mock()
130+
ps1 = Mock()
131+
ps1.__str__ = Mock(return_value="primary")
132+
ps2 = Mock()
133+
ps2.__str__ = Mock(return_value="secondary")
134+
135+
result = pyrepl_simple_interact.multiline_input(more_lines, ps1, ps2)
136+
137+
assert result == "statement"
138+
original_multiline_input.assert_called_once_with(more_lines, "primary", "secondary")
139+
140+
118141
def test_prompt_survives_shadowed_builtins_under_pythonstartup():
119142
# PYTHONSTARTUP executes pythonrc's source directly inside the real
120143
# REPL's __main__ namespace, not as an imported module. The tests

0 commit comments

Comments
 (0)