Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Lib/_pyrepl/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,13 @@ def calc_screen(self) -> list[str]:
if self.last_refresh_cache.valid(self):
offset, num_common_lines = self.last_refresh_cache.get_cached_location(self)

screen = self.last_refresh_cache.screen
screen = self.last_refresh_cache.screen.copy()
del screen[num_common_lines:]

screeninfo = self.last_refresh_cache.screeninfo
screeninfo = self.last_refresh_cache.screeninfo.copy()
del screeninfo[num_common_lines:]

last_refresh_line_end_offsets = self.last_refresh_cache.line_end_offsets
last_refresh_line_end_offsets = self.last_refresh_cache.line_end_offsets.copy()
Copy link

@kemingy kemingy Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this last_refresh_line_end_offsets has never been used in this function. The bug can be fixed by deleting this one.

The other two screen & screeninfo are synced back to self.last_refresh_cache. But for readability & maintenance, we should copy them here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

del last_refresh_line_end_offsets[num_common_lines:]

pos = self.pos
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_pyrepl/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,33 @@ def test_setpos_from_xy_for_non_printing_char(self):
reader.setpos_from_xy(8, 0)
self.assertEqual(reader.pos, 7)

def test_prompt_with_newline_no_duplicate(self):
# gh-127068: prompts with newlines should not duplicate
# The bug is that screen = cache.screen creates a reference, not a copy,
from unittest.mock import MagicMock

console = MagicMock()
console.height = 100
console.width = 80

reader = prepare_reader(console)

screen = reader.calc_screen()
reader.last_refresh_cache.update_cache(reader, screen, reader.screeninfo)

cache_screen_before = reader.last_refresh_cache.screen
original_cache_content = cache_screen_before.copy()

reader.insert("h")
screen = reader.calc_screen()

self.assertEqual(
cache_screen_before, original_cache_content,
f"Cache was modified during calc_screen! "
f"Original: {original_cache_content}, Now: {cache_screen_before}"
)


@force_colorized_test_class
class TestReaderInColor(ScreenEqualMixin, TestCase):
def test_syntax_highlighting_basic(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix an issue in default REPL where prompts containing newlines would be duplicated
on each keypress.
Loading