gh-154470: Fix spurious ^J in pdb's colorized list command (#154473) · python/cpython@c68ce16 · GitHub
Skip to content

Commit c68ce16

Browse files
authored
gh-154470: Fix spurious ^J in pdb's colorized list command (#154473)
1 parent e03ed46 commit c68ce16

4 files changed

Lines changed: 68 additions & 1 deletion

File tree

Lib/pdb.py

Lines changed: 5 additions & 1 deletion

Lib/test/test_pdb.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4996,6 +4996,16 @@ def test_code_display(self):
49964996
p.set_trace(commands=['ll', 'c'])
49974997
self.assertNotIn("\x1b", output.getvalue())
49984998

4999+
def test_list_does_not_colorize_trailing_newlines(self):
5000+
# Keep the marker split so it is not present in the listed source.
5001+
caret_newline = "^" + "J"
5002+
output = io.StringIO()
5003+
p = pdb.Pdb(stdout=output, colorize=True)
5004+
p.set_trace(commands=['list', 'continue'])
5005+
result = output.getvalue()
5006+
self.assertIn("\x1b", result)
5007+
self.assertNotIn(caret_newline, result)
5008+
49995009
def test_stack_entry(self):
50005010
output = io.StringIO()
50015011
p = pdb.Pdb(stdout=output, colorize=True)

Lib/test/test_remote_pdb.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,55 @@ def test_handle_eof(self):
13371337
self.assertEqual(process.returncode, 0)
13381338
self.assertEqual(stderr, "")
13391339

1340+
def test_colorized_list_has_no_caret_encoded_newlines(self):
1341+
"""A colorized ``list`` must not append "^J" to each source line.
1342+
1343+
The remote server colorizes the source it sends to the client. The
1344+
colorizer renders control characters in caret notation, so a source
1345+
line's trailing newline has to be stripped *before* it is colorized;
1346+
otherwise every listed line ends with a spurious "^J". ``where`` was
1347+
unaffected because it strips the line before colorizing. See
1348+
gh-154470.
1349+
"""
1350+
# colorize=True is what attaching from a color-capable terminal passes
1351+
# to the server, and it is what makes the server colorize ``list``.
1352+
script = textwrap.dedent(f"""
1353+
import pdb, sys
1354+
def helper():
1355+
x = 42
1356+
return x
1357+
def connect():
1358+
frame = sys._getframe()
1359+
pdb._connect(
1360+
host='127.0.0.1',
1361+
port={self.port},
1362+
frame=frame,
1363+
commands="",
1364+
version=pdb._PdbServer.protocol_version(),
1365+
signal_raising_thread=False,
1366+
colorize=True,
1367+
)
1368+
return helper()
1369+
connect()
1370+
""")
1371+
self._create_script(script=script)
1372+
process, client_file = self._connect_and_get_client_file()
1373+
1374+
with kill_on_error(process):
1375+
self._read_until_prompt(client_file)
1376+
self._send_command(client_file, "l 1, 15")
1377+
messages = self._read_until_prompt(client_file)
1378+
source = "".join(m["message"] for m in messages if "message" in m)
1379+
1380+
# Sanity: we really did receive colorized source ...
1381+
self.assertIn("helper", source)
1382+
self.assertIn("\x1b[", source) # ANSI color escapes are present
1383+
# ... and no trailing newline leaked through as caret notation.
1384+
self.assertNotIn("^J", source)
1385+
self._send_command(client_file, "c")
1386+
process.wait(timeout=SHORT_TIMEOUT)
1387+
self.assertEqual(process.returncode, 0)
1388+
13401389
@unittest.skipUnless(pty, "requires pty")
13411390
def test_prompt_with_interactive_terminal(self):
13421391
"""The server must send "(Pdb) " even when the target owns a terminal.
Lines changed: 4 additions & 0 deletions

0 commit comments

Comments
 (0)