`structseq_repr` mislabels unnamed `PyStructSequence` fields (e.g. `os.stat_result` slots 7–9)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 35.9k
- PR merge metrics
- PR metrics pending
Description
Bug report
Bug description:
structseq_repr() indexes tp_members by visible sequence position, but tp_members is built with every PyStructSequence_UnnamedField entry skipped (packed). As a result, every visible slot at or after the first unnamed field is labeled with the name of a later (hidden) field. For os.stat_result, the unnamed integer time slots 7–9 borrow the names of the hidden st_atime/st_mtime/st_ctime float fields.
Real-world case — a plain os.stat(). The repr prints st_atime=<integer seconds>, but the st_atime attribute is the float with sub-second precision — they are different fields:
>>> import os
>>> st = os.stat(os.curdir)
>>> st
os.stat_result(st_mode=16877, st_ino=1367263, st_dev=16777232, st_nlink=43,
st_uid=501, st_gid=20, st_size=1376,
st_atime=1784619363, st_mtime=1784619363, st_ctime=1784619363)
>>> st[7] # sequence slot 7 — the integer atime
1784619363
>>> st.st_atime # the .st_atime attribute — a different, hidden float field
1784619363.4590902
Synthetic case, isolating the mechanism with distinct values per field:
>>> # The repr always labels sequence slot 7 "st_atime", printing slot 7's value:
>>> st = os.stat_result(range(22))
>>> st
os.stat_result(st_mode=0, st_ino=1, st_dev=2, st_nlink=3, st_uid=4, st_gid=5,
st_size=6, st_atime=7, st_mtime=8, st_ctime=9)
>>> st[7]
7
>>> # But the .st_atime ATTRIBUTE is the hidden field at index 10:
>>> st.st_atime # field 10 supplied -> 10
10 # surprise! differs from the repr above
>>> os.stat_result(range(11)).st_atime # field 10 supplied -> 10
10
>>> os.stat_result(range(10)).st_atime # field 10 NOT supplied -> falls back to slot 7 -> 7
7 # surprise! # **UPDATE**: this is an intentional behavior in overrided `statresult_new`
>>> os.stat_result.n_sequence_fields, os.stat_result.n_unnamed_fields
(10, 3)
>>> os.stat_result.__match_args__ # correctly excludes the 3 unnamed slots
('st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', 'st_size')
The mislabel is easy to miss: when only the 10 sequence fields are supplied (range(10)), the hidden float st_atime (index 10) is unset and derives from the integer slot 7, so the repr and the attribute coincidentally agree. Supplying field 10 (range(11) or more, or a real os.stat()) makes them disagree.
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Linked PRs
- gh-154434
- gh-154684
- gh-154687
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at structseq_repr and reproduce the mismatch with the os.stat_result examples in the issue, including supplied hidden fields. Check that visible sequence slots are labeled independently of unnamed fields while hidden attributes retain their values, then verify the behavior against the linked PRs and the documented examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100