Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3412,7 +3412,7 @@ features:
Information about a file returned by :func:`os.statx`.

:class:`!statx_result` has all the attributes that :class:`~stat_result` has
on Linux, making it :term:`duck-typing` compatible, but
on Linux, but using ``stx_`` prefix instead of ``st_``.
:class:`!statx_result` is not a subclass of :class:`~stat_result` and cannot
be used as a tuple.

Expand Down
54 changes: 28 additions & 26 deletions Lib/test/test_os/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,8 @@ def check_statx_attributes(self, filename):
result = os.statx(filename, maximal_mask)
basic_result = os.stat(filename)

time_attributes = ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime')
time_attributes = ('stx_atime', 'stx_mtime', 'stx_ctime',
'stx_birthtime')
# gh-83714: st_birthtime can be None on tmpfs even if STATX_BTIME mask
# is used
time_attributes = [name for name in time_attributes
Expand All @@ -759,43 +760,44 @@ def check_statx_attributes(self, filename):

# Check that valid attributes match os.stat.
requirements = (
('st_mode', os.STATX_TYPE | os.STATX_MODE),
('st_nlink', os.STATX_NLINK),
('st_uid', os.STATX_UID),
('st_gid', os.STATX_GID),
('st_atime', os.STATX_ATIME),
('st_atime_ns', os.STATX_ATIME),
('st_mtime', os.STATX_MTIME),
('st_mtime_ns', os.STATX_MTIME),
('st_ctime', os.STATX_CTIME),
('st_ctime_ns', os.STATX_CTIME),
('st_ino', os.STATX_INO),
('st_size', os.STATX_SIZE),
('st_blocks', os.STATX_BLOCKS),
('st_birthtime', os.STATX_BTIME),
('st_birthtime_ns', os.STATX_BTIME),
('stx_mode', os.STATX_TYPE | os.STATX_MODE),
('stx_nlink', os.STATX_NLINK),
('stx_uid', os.STATX_UID),
('stx_gid', os.STATX_GID),
('stx_atime', os.STATX_ATIME),
('stx_atime_ns', os.STATX_ATIME),
('stx_mtime', os.STATX_MTIME),
('stx_mtime_ns', os.STATX_MTIME),
('stx_ctime', os.STATX_CTIME),
('stx_ctime_ns', os.STATX_CTIME),
('stx_ino', os.STATX_INO),
('stx_size', os.STATX_SIZE),
('stx_blocks', os.STATX_BLOCKS),
('stx_birthtime', os.STATX_BTIME),
('stx_birthtime_ns', os.STATX_BTIME),
# unconditionally valid members
('st_blksize', 0),
('st_rdev', 0),
('st_dev', 0),
('stx_blksize', 0),
('stx_rdev', 0),
('stx_dev', 0),
)
for name, bits in requirements:
if result.stx_mask & bits == bits and hasattr(basic_result, name):
st_name = "st_" + name[4:]
if result.stx_mask & bits == bits and hasattr(basic_result, st_name):
x = getattr(result, name)
b = getattr(basic_result, name)
b = getattr(basic_result, st_name)
self.assertEqual(type(x), type(b))
if isinstance(x, float):
self.assertAlmostEqual(x, b, msg=name)
else:
self.assertEqual(x, b, msg=name)

self.assertEqual(result.stx_rdev_major, os.major(result.st_rdev))
self.assertEqual(result.stx_rdev_minor, os.minor(result.st_rdev))
self.assertEqual(result.stx_dev_major, os.major(result.st_dev))
self.assertEqual(result.stx_dev_minor, os.minor(result.st_dev))
self.assertEqual(result.stx_rdev_major, os.major(result.stx_rdev))
self.assertEqual(result.stx_rdev_minor, os.minor(result.stx_rdev))
self.assertEqual(result.stx_dev_major, os.major(result.stx_dev))
self.assertEqual(result.stx_dev_minor, os.minor(result.stx_dev))

members = [name for name in dir(result)
if name.startswith('st_') or name.startswith('stx_')]
if name.startswith('stx_')]
for name in members:
try:
setattr(result, name, 1)
Expand Down
15 changes: 10 additions & 5 deletions Lib/test/test_os/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1672,17 +1672,22 @@ def test_chown_dir_fd(self):
with self.prepare_file() as (dir_fd, name, fullname):
posix.chown(name, os.getuid(), os.getgid(), dir_fd=dir_fd)

def check_statlike_dir_fd(self, func):
def check_statlike_dir_fd(self, func, prefix):
with self.prepare() as (dir_fd, name, fullname):
with open(fullname, 'w') as outfile:
outfile.write("testline\n")
self.addCleanup(posix.unlink, fullname)

def get(result, attr):
return getattr(result, prefix + attr)

s1 = func(fullname)
s2 = func(name, dir_fd=dir_fd)
self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino))
self.assertEqual((get(s1, "dev"), get(s1, "ino")),
(get(s2, "dev"), get(s2, "ino")))
s2 = func(fullname, dir_fd=None)
self.assertEqual((s1.st_dev, s1.st_ino), (s2.st_dev, s2.st_ino))
self.assertEqual((get(s1, "dev"), get(s1, "ino")),
(get(s2, "dev"), get(s2, "ino")))

self.assertRaisesRegex(TypeError, 'should be integer or None, not',
func, name, dir_fd=posix.getcwd())
Expand All @@ -1700,13 +1705,13 @@ def check_statlike_dir_fd(self, func):

@unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()")
def test_stat_dir_fd(self):
self.check_statlike_dir_fd(posix.stat)
self.check_statlike_dir_fd(posix.stat, prefix="st_")

@unittest.skipUnless(hasattr(posix, 'statx'), "test needs os.statx()")
def test_statx_dir_fd(self):
def func(path, **kwargs):
return posix.statx(path, os.STATX_INO, **kwargs)
self.check_statlike_dir_fd(func)
self.check_statlike_dir_fd(func, prefix="stx_")

@unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()")
def test_utime_dir_fd(self):
Expand Down
64 changes: 32 additions & 32 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3330,17 +3330,17 @@ typedef struct {

static PyMemberDef pystatx_result_members[] = {
MM(stx_mask, Py_T_UINT, mask, "member validity mask"),
MM(st_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"),
MM(stx_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"),
MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"),
MM(st_mode, Py_T_USHORT, mode, "protection bits"),
MM(stx_mode, Py_T_USHORT, mode, "protection bits"),
MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask,
"Mask of supported bits in stx_attributes"),
MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"),
MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"),
MX(st_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"),
MX(stx_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"),
MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"),
MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"),
MX(st_dev, Py_T_ULONGLONG, dev, "device"),
MX(stx_dev, Py_T_ULONGLONG, dev, "device"),
{NULL},
};

Expand All @@ -3361,9 +3361,9 @@ static PyMemberDef pystatx_result_members[] = {
return PyLong_FromUnsignedLong(value); \
}

STATX_GET_UINT(st_uid, stx_uid, STATX_UID)
STATX_GET_UINT(st_gid, stx_gid, STATX_GID)
STATX_GET_UINT(st_nlink, stx_nlink, STATX_NLINK)
STATX_GET_UINT(stx_uid, stx_uid, STATX_UID)
STATX_GET_UINT(stx_gid, stx_gid, STATX_GID)
STATX_GET_UINT(stx_nlink, stx_nlink, STATX_NLINK)
#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN
STATX_GET_UINT(stx_dio_mem_align, stx_dio_mem_align, STATX_DIOALIGN)
STATX_GET_UINT(stx_dio_offset_align, stx_dio_offset_align, STATX_DIOALIGN)
Expand Down Expand Up @@ -3398,9 +3398,9 @@ STATX_GET_UINT(stx_atomic_write_unit_max_opt, stx_atomic_write_unit_max_opt,
return PyLong_FromUnsignedLongLong(value); \
}

STATX_GET_ULONGLONG(st_blocks, stx_blocks, STATX_BLOCKS)
STATX_GET_ULONGLONG(st_ino, stx_ino, STATX_INO)
STATX_GET_ULONGLONG(st_size, stx_size, STATX_SIZE)
STATX_GET_ULONGLONG(stx_blocks, stx_blocks, STATX_BLOCKS)
STATX_GET_ULONGLONG(stx_ino, stx_ino, STATX_INO)
STATX_GET_ULONGLONG(stx_size, stx_size, STATX_SIZE)
#ifdef HAVE_STRUCT_STATX_STX_MNT_ID
STATX_GET_ULONGLONG(stx_mnt_id, stx_mnt_id, STATX_MNT_ID)
#endif
Expand All @@ -3421,10 +3421,10 @@ STATX_GET_ULONGLONG(stx_subvol, stx_subvol, STATX_SUBVOL)
return PyFloat_FromDouble(sec); \
}

STATX_GET_DOUBLE(st_atime, atime_sec, STATX_ATIME)
STATX_GET_DOUBLE(st_birthtime, btime_sec, STATX_BTIME)
STATX_GET_DOUBLE(st_ctime, ctime_sec, STATX_CTIME)
STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME)
STATX_GET_DOUBLE(stx_atime, atime_sec, STATX_ATIME)
STATX_GET_DOUBLE(stx_birthtime, btime_sec, STATX_BTIME)
STATX_GET_DOUBLE(stx_ctime, ctime_sec, STATX_CTIME)
STATX_GET_DOUBLE(stx_mtime, mtime_sec, STATX_MTIME)

#define STATX_GET_NSEC(ATTR, MEMBER, MASK) \
static PyObject* \
Expand All @@ -3440,29 +3440,29 @@ STATX_GET_DOUBLE(st_mtime, mtime_sec, STATX_MTIME)
return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \
}

STATX_GET_NSEC(st_atime_ns, stx_atime, STATX_ATIME)
STATX_GET_NSEC(st_birthtime_ns, stx_btime, STATX_BTIME)
STATX_GET_NSEC(st_ctime_ns, stx_ctime, STATX_CTIME)
STATX_GET_NSEC(st_mtime_ns, stx_mtime, STATX_MTIME)
STATX_GET_NSEC(stx_atime_ns, stx_atime, STATX_ATIME)
STATX_GET_NSEC(stx_birthtime_ns, stx_btime, STATX_BTIME)
STATX_GET_NSEC(stx_ctime_ns, stx_ctime, STATX_CTIME)
STATX_GET_NSEC(stx_mtime_ns, stx_mtime, STATX_MTIME)

#define G(attr, doc) \
{#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL}

static PyGetSetDef pystatx_result_getset[] = {
G(st_nlink, "number of hard links"),
G(st_uid, "user ID of owner"),
G(st_gid, "group ID of owner"),
G(st_ino, "inode"),
G(st_size, "total size, in bytes"),
G(st_blocks, "number of blocks allocated"),
G(st_atime, "time of last access"),
G(st_atime_ns, "time of last access in nanoseconds"),
G(st_birthtime, "time of creation"),
G(st_birthtime_ns, "time of creation in nanoseconds"),
G(st_ctime, "time of last change"),
G(st_ctime_ns, "time of last change in nanoseconds"),
G(st_mtime, "time of last modification"),
G(st_mtime_ns, "time of last modification in nanoseconds"),
G(stx_nlink, "number of hard links"),
G(stx_uid, "user ID of owner"),
G(stx_gid, "group ID of owner"),
G(stx_ino, "inode"),
G(stx_size, "total size, in bytes"),
G(stx_blocks, "number of blocks allocated"),
G(stx_atime, "time of last access"),
G(stx_atime_ns, "time of last access in nanoseconds"),
G(stx_birthtime, "time of creation"),
G(stx_birthtime_ns, "time of creation in nanoseconds"),
G(stx_ctime, "time of last change"),
G(stx_ctime_ns, "time of last change in nanoseconds"),
G(stx_mtime, "time of last modification"),
G(stx_mtime_ns, "time of last modification in nanoseconds"),
#ifdef HAVE_STRUCT_STATX_STX_MNT_ID
G(stx_mnt_id, "mount ID"),
#endif
Expand Down
Loading