gh-154387: fix structseq_repr by labeling unnamed fields in repr as <unnamed@N>#154687
Open
XuehaiPan wants to merge 9 commits into
Open
gh-154387: fix structseq_repr by labeling unnamed fields in repr as <unnamed@N>#154687XuehaiPan wants to merge 9 commits into
structseq_repr by labeling unnamed fields in repr as <unnamed@N>#154687XuehaiPan wants to merge 9 commits into
Conversation
structseq_repr() labeled each visible slot by indexing tp_members by slot position, but tp_members is packed with unnamed fields skipped, so every slot at or after the first unnamed field borrowed the name of a later hidden field. For os.stat_result the integer time slots printed as st_atime/st_mtime/st_ctime (which are actually separate hidden float fields), so the repr disagreed with both st[i] and the .st_atime attribute. Walk the members alongside the visible slots and label unnamed slots positionally as "<unnamed@N>", which is correct regardless of where the unnamed fields sit among the visible ones.
<unnamed@N>structseq_repr by labeling unnamed fields in repr as <unnamed@N>
Lower n_in_sequence to 4 in the _testcapi interspersed helper so the trailing
named field ("fifth") is hidden, and expand
test_repr_with_interspersed_unnamed_fields to assert the full surface: repr
(hidden field excluded), field counts, __match_args__, tuple()/len(), and
attribute access to visible and hidden fields (the latter both provided and
defaulted to None).
os.stat_result has three unnamed integer time slots (7-9) whose values are mirrored by the named float fields st_atime/st_mtime/st_ctime (statresult_new fills the latter from the former). The generic struct sequence repr shows those unnamed slots as <unnamed@7..9>, which is uninformative. Add statresult_repr showing the named fields (st_mode..st_size and the float st_atime/st_mtime/st_ctime) by name, matching the attributes. Install it through the type dict via PyObject_SetAttrString so repr() and st.__repr__() resolve to the same function; a raw tp_repr assignment would leave the __repr__ wrapper readied by PyStructSequence_NewType pointing at the generic repr. Other struct sequences keep the generic <unnamed@N>.
XuehaiPan
marked this pull request as ready for review
July 25, 2026 17:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Walk the members alongside the visible slots and label unnamed slots positionally as
<unnamed@N>, which is correct regardless of where the unnamed fields sit among the visible ones.Before this PR, the member slots are accessed incorrectly in
reprwhen there are unnamed fields. It can cause a spuriousSystemErrorwhen the index reaches the number of named fields (n_fields - n_unnamed_fields), or display the unnamed fields with<name>=<value>using a later field's name.For example, a structseq type with
WithUnnamed(first, <unnamed>, third, <unnamed>) + [hidden .fifth], before this PR:After this PR:
Before this PR, for
os.stat_result, the unnamed slots printed as the following field namesst_atime/st_mtime/st_ctime.After this PR, a new custom
statresult_repris added analogously tostatresult_new. The customreprshows the fields 10-12 (always initialized instatresult_new) instead of generic<unnamed@N>.Without the custom
statresult_repr, the generic<unnamed@N>will be used, which is not that legible.Fixes #154387
structseq_reprmislabels unnamedPyStructSequencefields (e.g.os.stat_resultslots 7–9) #154387