Skip to content

Deprecate the tuple API (object[index]) of structseq types (ex: os.stat_result) #155358

Description

@vstinner

Hi,

I propose deprecating the tuple API (object[index]) of the following structseq objects:

  • _interpchannels.ChannelInfo
  • _lsprof.profiler_entry
  • _lsprof.profiler_subentry
  • _thread._ExceptHookArgs
  • grp.struct_group
  • os.sched_param
  • os.stat_result
  • os.statvfs_result
  • os.terminal_size
  • os.times_result
  • os.uname_result
  • os.waitid_result
  • pwd.struct_passwd
  • resource.struct_rusage
  • signal.struct_siginfo
  • sys.UnraisableHookArgs
  • sys._emscripten_info
  • sys.asyncgen_hooks
  • sys.flags
  • sys.float_info
  • sys.getwindowsversion
  • sys.hash_info
  • sys.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

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions