Skip to content

fix: propagate sequence end length errors - #6234

Open
virtuald wants to merge 3 commits into
pybind:masterfrom
virtuald:fix/capi-sequence-end-errors
Open

virtuald wants to merge 3 commits into
pybind:masterfrom
virtuald:fix/capi-sequence-end-errors

Conversation

@virtuald

@virtuald virtuald commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

code and description is 🤖 generated, but it looks right. Related to #2862

Description

Check the signed PySequence_Size result before constructing sequence::end()'s iterator. This preserves length exceptions instead of producing an invalid end offset with a pending Python error.

Suggested changelog entry:

  • Make sequence::end() propagate length-query failures rather than constructing an invalid end iterator.

@rwgk rwgk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Beyond the scope of this PR, but I want to log my thoughts:

Looking at the error-clearing code in this test, and the review of #6228 yesterday, made me wonder: How many similar situations do we have in production code?

PyErr_Occurred() detects all Python exceptions, including KeyboardInterrupt and SystemExit. Paths that then clear or replace the error can inadvertently suppress those exceptions. There are examples worth revisiting in py::getattr(..., default) and py::len_hint().

Two ideas to record for potential follow-ups:

  • When replacing an exception to provide additional context, use py::raise_from to preserve the original cause. That would at least make a swallowed Ctrl-C visible in the reported exception chain.
  • Consider a helper with semantics similar to Python's except Exception, for fallback paths that currently use raw PyErr_Occurred() checks. It should propagate other BaseException subclasses automatically. Some callers may need narrower handling still, such as suppressing only AttributeError.

Comment thread tests/test_pytypes.cpp Outdated
Comment on lines +472 to +473
PyErr_Clear();
PyErr_SetString(PyExc_AssertionError, "sequence::end returned with a pending error");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
PyErr_Clear();
PyErr_SetString(PyExc_AssertionError, "sequence::end returned with a pending error");
py::raise_from(PyExc_AssertionError,
"sequence::end returned with a pending error");

py::raise_from is already public API. This preserves the original exception and its traceback as the cause of the assertion failure. In particular, a KeyboardInterrupt will remain visible in the exception chain.

This still wraps the interrupt in an AssertionError, but it seems like a reasonable scoped improvement for this test. The existing throw py::error_already_set() below should stay: raise_from sets the Python error indicator without throwing.

@virtuald

virtuald commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Looking at the error-clearing code in this test, and the review of #6228 yesterday, made me wonder: How many similar situations do we have in production code?

@rwgk someone's comment on #2862 (which I filed a long time ago) is what got me thinking about that exact thing. Astra did a scan and I had it create a separate branch for each fix it identified... there are 27 branches that I have locally. So... yeah, this potentially occurs a lot. I'll be pushing some of those branches tonight as PRs, hopefully.

This branch has not been deployed

No deployments
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.

2 participants