Skip to content

Conversation

@mohsinm-dev
Copy link
Contributor

@mohsinm-dev mohsinm-dev commented Nov 10, 2025

Summary

  • Fix assertion failure in io.TextIOWrapper.tell() when reading files ending with standalone \r
  • Add comprehensive test case to prevent regression
  • Add NEWS entry documenting the fix

Root Cause

The tell() optimization algorithm assumed buffered data would always be available when calculating position, but files ending with standalone \r create empty snapshots, causing assert(skip_back <= PyBytes_GET_SIZE(next_input)) to fail.

Fix

Added check to skip optimization when next_input is empty, preventing the assertion failure while maintaining correct position calculation.

Test Plan

  • Added test case test_tell_after_readline_with_cr in test_textio.py
  • Verified fix handles standalone carriage returns correctly
  • Confirmed all existing io tests still pass
  • Built CPython with debug assertions enabled

…dalone carriage return

When TextIOWrapper.tell() is called after reading a line that ends with a
standalone carriage return (\r), the tell optimization algorithm incorrectly
assumes there is buffered data to search through. This causes an assertion
failure when skip_back=1 exceeds the empty buffer size.

The fix detects when next_input is empty and skips the optimization phase,
falling back to the byte-by-byte decoding method which always works correctly.
This properly handles the architectural constraint that buffer optimization
cannot function without buffered data.
…dalone carriage return

Add test case and fix assertion failure in TextIOWrapper.tell() when reading
files that end with a standalone carriage return (\r). The optimization
algorithm incorrectly assumed buffered data would always be available,
causing an assertion failure when next_input is empty.
- Remove trailing whitespace
- Add missing newline at end of NEWS file
@serhiy-storchaka serhiy-storchaka self-requested a review November 10, 2025 13:14
Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

The cause of this bug is a simple typo.

    assert(skip_bytes <= PyBytes_GET_SIZE(next_input));

remaining = f.read()
self.assertEqual(remaining, "")

def test_tell_after_readline_with_multiple_cr(self):
Copy link
Member

Choose a reason for hiding this comment

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

Is this test necessary?

def test_tell_after_readline_with_cr(self):
# Test for gh-141314: TextIOWrapper.tell() assertion failure
# when dealing with standalone carriage returns
data = b'line1=1\r'
Copy link
Member

Choose a reason for hiding this comment

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

It can be simply b'line1\r'

@bedevere-app
Copy link

bedevere-app bot commented Nov 10, 2025

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Copy link
Contributor

@sergey-miryanov sergey-miryanov left a comment

Choose a reason for hiding this comment

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

Please revert unnecessary changes in test_textio.py

- Fix assertion to check skip_bytes instead of skip_back
- Use simpler test data b'line1\r' instead of b'line1=1\r'
- Remove unnecessary multiple CR test case
- Clean up workaround code

The assertion was checking wrong variable (skip_back vs skip_bytes).
skip_back is search step size, skip_bytes is buffer offset needing validation.
@mohsinm-dev mohsinm-dev force-pushed the fix-textio-tell-assertion-gh-141314 branch from b298058 to e84d686 Compare November 10, 2025 17:53
Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

LGTM, 👍

Copy link
Contributor

@sergey-miryanov sergey-miryanov left a comment

Choose a reason for hiding this comment

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

Thanks!

@serhiy-storchaka serhiy-storchaka merged commit af80fac into python:main Nov 11, 2025
49 of 50 checks passed
@serhiy-storchaka serhiy-storchaka added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes labels Nov 11, 2025
@miss-islington-app
Copy link

Thanks @mohsinm-dev for the PR, and @serhiy-storchaka for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@miss-islington-app
Copy link

Thanks @mohsinm-dev for the PR, and @serhiy-storchaka for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖

@miss-islington-app
Copy link

Sorry, @mohsinm-dev and @serhiy-storchaka, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker af80fac42548719ede7241bfbab3c2c0775b4760 3.13

@miss-islington-app
Copy link

Sorry, @mohsinm-dev and @serhiy-storchaka, I could not cleanly backport this to 3.14 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker af80fac42548719ede7241bfbab3c2c0775b4760 3.14

@cmaloney
Copy link
Contributor

The cherry picks conflicts are probably because of the io test movement, happy to work on them if you'd like

@mohsinm-dev
Copy link
Contributor Author

The cherry picks conflicts are probably because of the io test movement, happy to work on them if you'd like

If I am not wrong Lib/test/test_io/test_textio.py exists in main branch (where our commit adds tests). But it was deleted in 3.13 branch and 3.14 branch because the tests are still in test_general.py

@bedevere-app
Copy link

bedevere-app bot commented Nov 12, 2025

GH-141452 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Nov 12, 2025
@bedevere-app
Copy link

bedevere-app bot commented Nov 12, 2025

GH-141453 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Nov 12, 2025
mohsinm-dev added a commit to mohsinm-dev/cpython that referenced this pull request Nov 12, 2025
…th standalone carriage return (pythonGH-141331)

The assertion was checking wrong variable (skip_back vs skip_bytes).
(cherry picked from commit af80fac)

Co-authored-by: Mohsin Mehmood <55545648+mohsinm-dev@users.noreply.github.com>
@bedevere-app
Copy link

bedevere-app bot commented Nov 12, 2025

GH-141452 is a backport of this pull request to the 3.13 branch.

@bedevere-app
Copy link

bedevere-app bot commented Nov 12, 2025

GH-141453 is a backport of this pull request to the 3.14 branch.

@efimov-mikhail
Copy link
Member

efimov-mikhail commented Nov 12, 2025

IMO, these backports are correct and we can merge them.

serhiy-storchaka pushed a commit that referenced this pull request Nov 12, 2025
…ndalone carriage return (GH-141331) (GH-141453)

The assertion was checking wrong variable (skip_back vs skip_bytes).
(cherry picked from commit af80fac)
serhiy-storchaka pushed a commit that referenced this pull request Nov 12, 2025
…ndalone carriage return (GH-141331) (GH-141452)

The assertion was checking wrong variable (skip_back vs skip_bytes).
(cherry picked from commit af80fac)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants