Hi,
I propose deprecating the tuple API (object[index]) of the following structseq objects:
_interpchannels.ChannelInfo_lsprof.profiler_entry_lsprof.profiler_subentry_thread._ExceptHookArgsgrp.struct_groupos.sched_paramos.stat_resultos.statvfs_resultos.terminal_sizeos.times_resultos.uname_resultos.waitid_resultpwd.struct_passwdresource.struct_rusagesignal.struct_siginfosys.UnraisableHookArgssys._emscripten_infosys.asyncgen_hookssys.flagssys.float_infosys.getwindowsversionsys.hash_infosys.int_info
The tuple API of the following structseq objects is kept, since it's useful:
sys.version_info: for example, sys.version_info[:2] is commonly used.curses.ncurses_version: same rationale than sys.version_info.time.struct_time: for example, y, m, d, hh, mm, ss = struct_time[:6] is used to unpack a time struct.
In the early days of Python, it was tedious to create an object with attributes in C. So to keep the implementation simple, many functions returned simply a tuple (trivial to build in C). Example on Python 1.5 (yes, Python version one!):
$ ./python
Python 1.5.2 (#1, Aug 7 2026, 17:03:12) [GCC 16.1.1 20260515 (Red Hat 16.1.1-2) on linux7
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import os
>>> st=os.stat(".")
>>> type(st)
<type 'tuple'>
>>> st
(16877, 60670004, 43, 1, 1000, 1000, 476, 1786114992, 1786114992, 1786114992)
The stat module provides indices to state struct members:
# Indices for stat struct members in the tuple returned by os.stat()
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
For example, st[stat.ST_INO] gets the inode number.
Over time, APIs became more and more complex and the structseq type was added to Python 2.2. It preserves the tuple API (object[index]), but adds also named attributes to object.
For example, st.st_ino gets the inode number. The tuple API is kept for backward compatibility.
os.stat_result has a complex history:
- At the beginning,
os.stat() simply returned a tuple of 10 integers. - Python 2.2 changed
os.stat() result type to os.stat_result to give access to attribute by name, but keep the tuple API for backward compatibility. - Python 2.3 added
os.stat_float_times(True) which allowed to get timestamps as float: timestamps remain integers by default. Only st.st_atime, st.st_ctime and st.st_mtime can be float. st[7], st[8] and st[9] are always integers, for backward compatibility. - Python 2.5 switched timestamps to float by default.
- Python 3.3 added
st_atime_ns, st_ctime_ns and st_mtime_ns.
So timestamps are stored in 3 formats in Python 3.15:
- integer (tuple API: seconds) for backward compatibility with Python 2.2 and older (yep, that's quite old!);
- float for
st_[acm]time (seconds); - integer for
st_[acm]time_ns (nanoseconds).
I would prefer to deprecate this tuple API. It's time to move on to named attributes introduced in Python 2.3!
Last years, new structseq objects were added with the tuple API even if these new objects didn't have to provide a backward compatibility with a previous tuple API. It's just because structseq doesn't give the choice, the tuple API is always provided.
By the way, later, _PyNamespace_New() and types.SimpleNamespace were added to Python 3.3 to create a simple object with attributes from a dictionary (attribute name => attribute value). For example, sys.implementation uses this API.
Linked PRs
Hi,
I propose deprecating the tuple API (
object[index]) of the following structseq objects:_interpchannels.ChannelInfo_lsprof.profiler_entry_lsprof.profiler_subentry_thread._ExceptHookArgsgrp.struct_groupos.sched_paramos.stat_resultos.statvfs_resultos.terminal_sizeos.times_resultos.uname_resultos.waitid_resultpwd.struct_passwdresource.struct_rusagesignal.struct_siginfosys.UnraisableHookArgssys._emscripten_infosys.asyncgen_hookssys.flagssys.float_infosys.getwindowsversionsys.hash_infosys.int_infoThe tuple API of the following structseq objects is kept, since it's useful:
sys.version_info: for example,sys.version_info[:2]is commonly used.curses.ncurses_version: same rationale thansys.version_info.time.struct_time: for example,y, m, d, hh, mm, ss = struct_time[:6]is used to unpack a time struct.In the early days of Python, it was tedious to create an object with attributes in C. So to keep the implementation simple, many functions returned simply a tuple (trivial to build in C). Example on Python 1.5 (yes, Python version one!):
The
statmodule provides indices to state struct members:For example,
st[stat.ST_INO]gets the inode number.Over time, APIs became more and more complex and the
structseqtype was added to Python 2.2. It preserves the tuple API (object[index]), but adds also named attributes to object.For example,
st.st_inogets the inode number. The tuple API is kept for backward compatibility.os.stat_resulthas a complex history:os.stat()simply returned a tuple of 10 integers.os.stat()result type toos.stat_resultto give access to attribute by name, but keep the tuple API for backward compatibility.os.stat_float_times(True)which allowed to get timestamps as float: timestamps remain integers by default. Onlyst.st_atime,st.st_ctimeandst.st_mtimecan be float.st[7],st[8]andst[9]are always integers, for backward compatibility.st_atime_ns,st_ctime_nsandst_mtime_ns.So timestamps are stored in 3 formats in Python 3.15:
st_[acm]time(seconds);st_[acm]time_ns(nanoseconds).I would prefer to deprecate this tuple API. It's time to move on to named attributes introduced in Python 2.3!
Last years, new
structseqobjects were added with the tuple API even if these new objects didn't have to provide a backward compatibility with a previous tuple API. It's just becausestructseqdoesn't give the choice, the tuple API is always provided.By the way, later,
_PyNamespace_New()andtypes.SimpleNamespacewere added to Python 3.3 to create a simple object with attributes from a dictionary (attribute name => attribute value). For example,sys.implementationuses this API.Linked PRs