https://github.com/python/cpython/commit/b84e0b7ce05f71de1dd017c899ba3a3d81fbdc0a
commit: b84e0b7ce05f71de1dd017c899ba3a3d81fbdc0a
branch: main
author: Jonathan J. Helmus <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-10-31T09:40:30Z
summary:
gh-158998: Fix `os.statx()` ignoring `follow_symlinks=False` (#158999)
files:
A Misc/NEWS.d/next/Library/2026-10-08-21-02-38.gh-issue-158998.DZYJI7.rst
M Lib/test/test_os/test_os.py
M Modules/posixmodule.c
diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py
index 81b3043eb7e75bc..79aa9e96dc91a6b 100644
--- a/Lib/test/test_os/test_os.py
+++ b/Lib/test/test_os/test_os.py
@@ -752,13 +752,13 @@ def test_stat_result_pickle(self):
unpickled = pickle.loads(p)
self.assertEqual(result, unpickled)
- def check_statx_attributes(self, filename):
+ def check_statx_attributes(self, filename, follow_symlinks=True):
maximal_mask = 0
for name in dir(os):
if name.startswith('STATX_'):
maximal_mask |= getattr(os, name)
- result = os.statx(filename, maximal_mask)
- stat_result = os.stat(filename)
+ result = os.statx(filename, maximal_mask,
follow_symlinks=follow_symlinks)
+ stat_result = os.stat(filename, follow_symlinks=follow_symlinks)
time_attributes = ('stx_atime', 'stx_btime', 'stx_ctime', 'stx_mtime')
# gh-83714: stx_btime can be None on tmpfs even if STATX_BTIME mask
@@ -873,6 +873,28 @@ def getmask(name):
def test_statx_attributes(self):
self.check_statx_attributes(self.fname)
+ @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()')
+ @os_helper.skip_unless_symlink
+ def test_statx_attributes_symlink(self):
+ link = self.fname + '-link'
+ os.symlink(os.path.basename(self.fname), link)
+ self.addCleanup(os_helper.unlink, link)
+
+ with self.subTest(broken=False, follow_symlinks=True):
+ self.check_statx_attributes(link, follow_symlinks=True)
+
+ with self.subTest(broken=False, follow_symlinks=False):
+ self.check_statx_attributes(link, follow_symlinks=False)
+
+ # break the symlink
+ os.unlink(self.fname)
+ with self.subTest(broken=True, follow_symlinks=True):
+ with self.assertRaises(FileNotFoundError):
+ os.statx(link, os.STATX_BASIC_STATS, follow_symlinks=True)
+
+ with self.subTest(broken=True, follow_symlinks=False):
+ self.check_statx_attributes(link, follow_symlinks=False)
+
@unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()')
def test_statx_attributes_bytes(self):
try:
diff --git
a/Misc/NEWS.d/next/Library/2026-10-08-21-02-38.gh-issue-158998.DZYJI7.rst
b/Misc/NEWS.d/next/Library/2026-10-08-21-02-38.gh-issue-158998.DZYJI7.rst
new file mode 100644
index 000000000000000..65ce9201fb01352
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-08-21-02-38.gh-issue-158998.DZYJI7.rst
@@ -0,0 +1 @@
+Fix :func:`os.statx` ignoring the *follow_symlinks* argument.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 503b237ab704c01..b78bc1683538312 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3673,6 +3673,9 @@ os_statx_impl(PyObject *module, path_t *path, unsigned
int mask, int flags,
"use dir_fd kwarg instead of AT_EMPTY_PATH flag");
return NULL;
}
+ if (!follow_symlinks) {
+ flags |= AT_SYMLINK_NOFOLLOW;
+ }
/* Future bits may refer to members beyond the current size of struct
statx, so we need to mask them off to prevent memory corruption. */
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]