| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks. Here is a first review.
Sorry, something went wrong.
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Victor Stinner <vstinner@python.org>
|
In addition to making the changes you directed, when modifying the sync= kwarg test to test the flags parameter, I moved that test to test_posix.py because test_os.py's header comment says it is
and os.statx is not portable. The os.statx_result tests are still in test_os.py because they reuse some of the code for testing os.stat_result in that file. The split seems quite arbitrary to me, but I'm sure there are historical reasons. |
Sorry, something went wrong.
|
I'm not fully comfortable with having some os.statx() tests in test_posix and some others in test_os. But it seems like you reused existing code in test_posix and test_os, so I would say that I'm fine with it. We might merge test_posix and test_os into a single test, but that's a different topic. |
Sorry, something went wrong.
I created #139322 for that :-) |
Sorry, something went wrong.
|
Do you still plan to merge test_os and test_posix, or have the tests stabilized enough that I should update this PR? |
Sorry, something went wrong.
|
The plan to split test_os is stale for now. You can rebase your PR. |
Sorry, something went wrong.
Co-authored-by: Victor Stinner <vstinner@python.org>
There was a problem hiding this comment.
Overall, the change LGTM. There is just one last open question of adding a default value to the mask parameter or not.
Sorry, something went wrong.
There was a problem hiding this comment.
Overall really like this. I don't have a strong preference around the defaults. I think it would be good to have a documentation expert check the new docs over.
Sorry, something went wrong.
Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
|
This API is uneasy to use. It took me a while to understand statx_result.stx_mask: all attributes seem to be available, but the value of most attributes is zero. It's surprising. Since we have stx_mask, we could make statx_result less surprising: either remove attribute if they are missing from stx_mask, or set their value to None instead of 0. I looked at the implementation and it might be possible to pack (mask, offset) as the context in pystatx_result_getset getters functions. mask is an unsigned int which can be 32-bit on 32-bit systems (32-bit pointers), which doesn't leave enough space for the 16-bit offset :-( To make the implementation simpler, I suppose that we should leave statx_result as it is: always expose all attributes, but set their value to 0 if they are missing from stx_mask. The C API (statx) is the same and Python usually makes os functions as thin wrappers to the C API. |
Sorry, something went wrong.
There was a problem hiding this comment.
Oh you should also document the new os.statx() function by adding a new "os" section near: https://docs.python.org/dev/whatsnew/3.15.html#os-path (Doc/whatsnew/3.15.rst).
Sorry, something went wrong.
Yes, I did this.
Done. |
Sorry, something went wrong.
|
👍 looking good to me with the kwarg doc change added in. |
Sorry, something went wrong.
Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
|
I enabled auto-merge, don't touch anything 😁 |
Sorry, something went wrong.
⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️Hi! The buildbot AMD64 CentOS9 NoGIL 3.x (tier-1) has failed when building commit fe9ac7f. What do you need to do:
You can take a look at the buildbot page here: https://buildbot.python.org/#/builders/1609/builds/3837 Summary of the results of the build (if available): Click to see traceback logsremote: Enumerating objects: 34, done.
remote: Counting objects: 3% (1/32)
remote: Counting objects: 6% (2/32)
remote: Counting objects: 9% (3/32)
remote: Counting objects: 12% (4/32)
remote: Counting objects: 15% (5/32)
remote: Counting objects: 18% (6/32)
remote: Counting objects: 21% (7/32)
remote: Counting objects: 25% (8/32)
remote: Counting objects: 28% (9/32)
remote: Counting objects: 31% (10/32)
remote: Counting objects: 34% (11/32)
remote: Counting objects: 37% (12/32)
remote: Counting objects: 40% (13/32)
remote: Counting objects: 43% (14/32)
remote: Counting objects: 46% (15/32)
remote: Counting objects: 50% (16/32)
remote: Counting objects: 53% (17/32)
remote: Counting objects: 56% (18/32)
remote: Counting objects: 59% (19/32)
remote: Counting objects: 62% (20/32)
remote: Counting objects: 65% (21/32)
remote: Counting objects: 68% (22/32)
remote: Counting objects: 71% (23/32)
remote: Counting objects: 75% (24/32)
remote: Counting objects: 78% (25/32)
remote: Counting objects: 81% (26/32)
remote: Counting objects: 84% (27/32)
remote: Counting objects: 87% (28/32)
remote: Counting objects: 90% (29/32)
remote: Counting objects: 93% (30/32)
remote: Counting objects: 96% (31/32)
remote: Counting objects: 100% (32/32)
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 5% (1/20)
remote: Compressing objects: 10% (2/20)
remote: Compressing objects: 15% (3/20)
remote: Compressing objects: 20% (4/20)
remote: Compressing objects: 25% (5/20)
remote: Compressing objects: 30% (6/20)
remote: Compressing objects: 35% (7/20)
remote: Compressing objects: 40% (8/20)
remote: Compressing objects: 45% (9/20)
remote: Compressing objects: 50% (10/20)
remote: Compressing objects: 55% (11/20)
remote: Compressing objects: 60% (12/20)
remote: Compressing objects: 65% (13/20)
remote: Compressing objects: 70% (14/20)
remote: Compressing objects: 75% (15/20)
remote: Compressing objects: 80% (16/20)
remote: Compressing objects: 85% (17/20)
remote: Compressing objects: 90% (18/20)
remote: Compressing objects: 95% (19/20)
remote: Compressing objects: 100% (20/20)
remote: Compressing objects: 100% (20/20), done.
remote: Total 34 (delta 13), reused 12 (delta 12), pack-reused 2 (from 2)
From https://github.com/python/cpython
* branch main -> FETCH_HEAD
Note: switching to 'fe9ac7fc8ca00515b3c9a4d91d7bbfe038c861e7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at fe9ac7fc8ca gh-83714: Implement os.statx() function (#139178)
Switched to and reset branch 'main'
configure: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module
./Modules/posixmodule.c:3325:50: error: ‘struct statx’ has no member named ‘stx_atomic_write_unit_max_opt’; did you mean ‘stx_atomic_write_unit_max’?
3325 | M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
| ^~~~
./Modules/posixmodule.c:3323:18: note: in definition of macro ‘M’
3323 | {attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
| ^~~~~~
./Modules/posixmodule.c:3369:5: note: in expansion of macro ‘MM’
3369 | MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
| ^~
./Modules/posixmodule.c: In function ‘pystatx_result_repr’:
./Modules/posixmodule.c:3427:26: warning: comparison of unsigned expression in ‘< 0’ is always false [-Wtype-limits]
3427 | for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) {
| ^
make: *** [Makefile:3799: Modules/posixmodule.o] Error 1
make: *** Waiting for unfinished jobs....
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
make: [Makefile:3389: clean-retain-profile] Error 1 (ignored) |
Sorry, something went wrong.
⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️Hi! The buildbot ARM64 Raspbian Debug 3.x (tier-3) has failed when building commit fe9ac7f. What do you need to do:
You can take a look at the buildbot page here: https://buildbot.python.org/#/builders/1829/builds/84 Summary of the results of the build (if available): Click to see traceback logsremote: Enumerating objects: 34, done.
remote: Counting objects: 3% (1/32)
remote: Counting objects: 6% (2/32)
remote: Counting objects: 9% (3/32)
remote: Counting objects: 12% (4/32)
remote: Counting objects: 15% (5/32)
remote: Counting objects: 18% (6/32)
remote: Counting objects: 21% (7/32)
remote: Counting objects: 25% (8/32)
remote: Counting objects: 28% (9/32)
remote: Counting objects: 31% (10/32)
remote: Counting objects: 34% (11/32)
remote: Counting objects: 37% (12/32)
remote: Counting objects: 40% (13/32)
remote: Counting objects: 43% (14/32)
remote: Counting objects: 46% (15/32)
remote: Counting objects: 50% (16/32)
remote: Counting objects: 53% (17/32)
remote: Counting objects: 56% (18/32)
remote: Counting objects: 59% (19/32)
remote: Counting objects: 62% (20/32)
remote: Counting objects: 65% (21/32)
remote: Counting objects: 68% (22/32)
remote: Counting objects: 71% (23/32)
remote: Counting objects: 75% (24/32)
remote: Counting objects: 78% (25/32)
remote: Counting objects: 81% (26/32)
remote: Counting objects: 84% (27/32)
remote: Counting objects: 87% (28/32)
remote: Counting objects: 90% (29/32)
remote: Counting objects: 93% (30/32)
remote: Counting objects: 96% (31/32)
remote: Counting objects: 100% (32/32)
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 5% (1/20)
remote: Compressing objects: 10% (2/20)
remote: Compressing objects: 15% (3/20)
remote: Compressing objects: 20% (4/20)
remote: Compressing objects: 25% (5/20)
remote: Compressing objects: 30% (6/20)
remote: Compressing objects: 35% (7/20)
remote: Compressing objects: 40% (8/20)
remote: Compressing objects: 45% (9/20)
remote: Compressing objects: 50% (10/20)
remote: Compressing objects: 55% (11/20)
remote: Compressing objects: 60% (12/20)
remote: Compressing objects: 65% (13/20)
remote: Compressing objects: 70% (14/20)
remote: Compressing objects: 75% (15/20)
remote: Compressing objects: 80% (16/20)
remote: Compressing objects: 85% (17/20)
remote: Compressing objects: 90% (18/20)
remote: Compressing objects: 95% (19/20)
remote: Compressing objects: 100% (20/20)
remote: Compressing objects: 100% (20/20), done.
remote: Total 34 (delta 13), reused 12 (delta 12), pack-reused 2 (from 2)
From https://github.com/python/cpython
* branch main -> FETCH_HEAD
Note: switching to 'fe9ac7fc8ca00515b3c9a4d91d7bbfe038c861e7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at fe9ac7fc8ca gh-83714: Implement os.statx() function (#139178)
Switched to and reset branch 'main'
configure: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module
./Modules/posixmodule.c:3325:50: error: ‘struct statx’ has no member named ‘stx_atomic_write_unit_max_opt’; did you mean ‘stx_atomic_write_unit_max’?
3325 | M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
| ^~~~
./Modules/posixmodule.c:3323:18: note: in definition of macro ‘M’
3323 | {attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
| ^~~~~~
./Modules/posixmodule.c:3369:5: note: in expansion of macro ‘MM’
3369 | MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
| ^~
./Modules/posixmodule.c: In function ‘pystatx_result_repr’:
./Modules/posixmodule.c:3427:26: warning: comparison of unsigned expression in ‘< 0’ is always false [-Wtype-limits]
3427 | for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) {
| ^
make: *** [Makefile:3812: Modules/posixmodule.o] Error 1
make: *** Waiting for unfinished jobs....
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
make: [Makefile:3395: clean-retain-profile] Error 1 (ignored) |
Sorry, something went wrong.
⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️Hi! The buildbot ARM Raspbian 3.x (tier-3) has failed when building commit fe9ac7f. What do you need to do:
You can take a look at the buildbot page here: https://buildbot.python.org/#/builders/424/builds/11822 Summary of the results of the build (if available): Click to see traceback logsremote: Enumerating objects: 34, done.
remote: Counting objects: 3% (1/32)
remote: Counting objects: 6% (2/32)
remote: Counting objects: 9% (3/32)
remote: Counting objects: 12% (4/32)
remote: Counting objects: 15% (5/32)
remote: Counting objects: 18% (6/32)
remote: Counting objects: 21% (7/32)
remote: Counting objects: 25% (8/32)
remote: Counting objects: 28% (9/32)
remote: Counting objects: 31% (10/32)
remote: Counting objects: 34% (11/32)
remote: Counting objects: 37% (12/32)
remote: Counting objects: 40% (13/32)
remote: Counting objects: 43% (14/32)
remote: Counting objects: 46% (15/32)
remote: Counting objects: 50% (16/32)
remote: Counting objects: 53% (17/32)
remote: Counting objects: 56% (18/32)
remote: Counting objects: 59% (19/32)
remote: Counting objects: 62% (20/32)
remote: Counting objects: 65% (21/32)
remote: Counting objects: 68% (22/32)
remote: Counting objects: 71% (23/32)
remote: Counting objects: 75% (24/32)
remote: Counting objects: 78% (25/32)
remote: Counting objects: 81% (26/32)
remote: Counting objects: 84% (27/32)
remote: Counting objects: 87% (28/32)
remote: Counting objects: 90% (29/32)
remote: Counting objects: 93% (30/32)
remote: Counting objects: 96% (31/32)
remote: Counting objects: 100% (32/32)
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 5% (1/20)
remote: Compressing objects: 10% (2/20)
remote: Compressing objects: 15% (3/20)
remote: Compressing objects: 20% (4/20)
remote: Compressing objects: 25% (5/20)
remote: Compressing objects: 30% (6/20)
remote: Compressing objects: 35% (7/20)
remote: Compressing objects: 40% (8/20)
remote: Compressing objects: 45% (9/20)
remote: Compressing objects: 50% (10/20)
remote: Compressing objects: 55% (11/20)
remote: Compressing objects: 60% (12/20)
remote: Compressing objects: 65% (13/20)
remote: Compressing objects: 70% (14/20)
remote: Compressing objects: 75% (15/20)
remote: Compressing objects: 80% (16/20)
remote: Compressing objects: 85% (17/20)
remote: Compressing objects: 90% (18/20)
remote: Compressing objects: 95% (19/20)
remote: Compressing objects: 100% (20/20)
remote: Compressing objects: 100% (20/20), done.
remote: Total 34 (delta 13), reused 12 (delta 12), pack-reused 2 (from 2)
From https://github.com/python/cpython
* branch main -> FETCH_HEAD
Note: switching to 'fe9ac7fc8ca00515b3c9a4d91d7bbfe038c861e7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at fe9ac7fc8ca gh-83714: Implement os.statx() function (#139178)
Switched to and reset branch 'main'
configure: WARNING: armv8l-unknown-linux-gnueabihf/gcc is not supported
configure: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module
configure: WARNING:
Platform "armv8l-unknown-linux-gnueabihf" with compiler "gcc" is not supported by the
CPython core team, see https://peps.python.org/pep-0011/ for more information.
./Modules/posixmodule.c: In function ‘stat_nanosecond_timestamp’:
./Modules/posixmodule.c:2638:31: warning: comparison is always true due to limited range of data type [-Wtype-limits]
2638 | if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) {
| ^~
./Modules/posixmodule.c:2638:45: warning: comparison is always true due to limited range of data type [-Wtype-limits]
2638 | if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) {
| ^~
./Modules/posixmodule.c: At top level:
./Modules/posixmodule.c:3325:50: error: ‘struct statx’ has no member named ‘stx_atomic_write_unit_max_opt’; did you mean ‘stx_atomic_write_unit_max’?
3325 | M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
| ^~~~
./Modules/posixmodule.c:3323:18: note: in definition of macro ‘M’
3323 | {attr, type, offset, Py_READONLY, PyDoc_STR(doc)}
| ^~~~~~
./Modules/posixmodule.c:3369:5: note: in expansion of macro ‘MM’
3369 | MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
| ^~
./Modules/posixmodule.c: In function ‘pystatx_result_repr’:
./Modules/posixmodule.c:3427:26: warning: comparison of unsigned expression in ‘< 0’ is always false [-Wtype-limits]
3427 | for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) {
| ^
make: *** [Makefile:3819: Modules/posixmodule.o] Error 1
make: *** Waiting for unfinished jobs....
Python/ceval.c: In function ‘_PyEvalFramePushAndInit_Ex’:
Python/ceval.c:1936:38: warning: ‘stack_array’ may be used uninitialized [-Wmaybe-uninitialized]
1936 | _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
| ^~~~~~~~~~~~~~~~~~~~~~~~
1937 | tstate, func, locals,
| ~~~~~~~~~~~~~~~~~~~~~
1938 | newargs, nargs, kwnames, previous
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1939 | );
| ~
Python/ceval.c:1859:1: note: by argument 4 of type ‘const _PyStackRef *’ to ‘_PyEvalFramePushAndInit’ declared here
1859 | _PyEvalFramePushAndInit(PyThreadState *tstate, _PyStackRef func,
| ^~~~~~~~~~~~~~~~~~~~~~~
Python/ceval.c:1905:17: note: ‘stack_array’ declared here
1905 | _PyStackRef stack_array[8];
| ^~~~~~~~~~~
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
find: ‘build’: No such file or directory
make: [Makefile:3397: clean-retain-profile] Error 1 (ignored) |
Sorry, something went wrong.
|
Oh no! AMD64 CentOS9 NoGIL 3.x buildbot build fails with: ./Modules/posixmodule.c:3325:50: error: ‘struct statx’ has no member named ‘stx_atomic_write_unit_max_opt’; did you mean ‘stx_atomic_write_unit_max’?
3325 | M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
| ^~~~
test.pythoninfo: os.uname: posix.uname_result(sysname='Linux', nodename='ip-172-31-1-186.us-west-1.compute.internal', release='5.14.0-612.el9.x86_64', version='#1 SMP PREEMPT_DYNAMIC Sat Aug 30 16:03:12 UTC 2025', machine='x86_64') platform.freedesktop_os_release[NAME]: CentOS Stream platform.freedesktop_os_release[VERSION]: 9 platform.libc_ver: glibc 2.34 platform.platform: Linux-5.14.0-612.el9.x86_64-x86_64-with-glibc2.34 ARM Raspbian 3.x and ARM64 Raspbian Debug 3.x also fail to build with: ./Modules/posixmodule.c:3325:50: error: ‘struct statx’ has no member named ‘stx_atomic_write_unit_max_opt’; did you mean ‘stx_atomic_write_unit_max’?
3325 | M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc)
| ^~~~
test.pythoninfo: os.uname: posix.uname_result(sysname='Linux', nodename='rpi5-bot', release='6.12.47+rpt-rpi-v8', version='#1 SMP PREEMPT Debian 1:6.12.47-1+rpt1~bookworm (2025-09-16)', machine='aarch64') platform.freedesktop_os_release[NAME]: Raspbian GNU/Linux platform.freedesktop_os_release[VERSION]: 12 (bookworm) platform.libc_ver: glibc 2.36 platform.platform: Linux-6.12.47+rpt-rpi-v8-aarch64-with-glibc2.36 |
Sorry, something went wrong.
|
@vstinner I know what's wrong and how to fix it. I'll have a PR up later today. |
Sorry, something went wrong.
Sorry, something went wrong.
Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
| Back | FazBrowse Home | New Git URL |
This PR implements os.statx, an interface to the Linux statx(2) system call introduced in kernel version 4.11 (April 2017) and first available in glibc version 2.28 (August 2018). This is derived from my earlier PR #136334 with the changes to os.stat removed, plus other changes (see below).
This PR implements the return value of os.statx with a custom C type statx_result, which contains a struct statx and uses member and getset descriptors to lazily create Python objects on attribute access. In a comment on the previous PR, @vstinner suggested using types.SimpleNamespace as the return value. I implemented that on the statx-simplenamespace branch in my fork. Using the benchmarks from this script (note they are not all fair comparisons):
It's slower than this PR when only size and mtime are requested, because it creates objects for the unconditionally valid members and both the float-seconds and int-nanoseconds timestamps. We could get some of this back by defining our own mask bits (bit 32 and above). As more bits are set in the mask and attributes are accessed, the gap widens. I'm not sure if that's due to dict resizing or slower attribute access or both, and I don't see how to improve either. The advantage of the namespace implementation is its simplicity, and any speed hacks would dilute that.
(Also, because it doesn't create unrequested members, it's not a perfect wrapper around the system call. For "real" use it doesn't matter, but for testing the syscall, you'll miss some quirks. For example, btrfs seems to always return atime and btime, but returns mtime and ctime (always together) only if mtime and/or ctime were requested.)
In this PR, most attributes are implemented with member descriptors pointing into the struct statx or a member in statx_result, so each attribute access on statx_result creates a new int or float object. My previous PR cached the created objects in the statx_result for the commonly-used attributes to avoid creating them more than once, which requires getset descriptors. Obviously, creating objects has a cost, but getset descriptors are slower than member descriptors. The crossover point turns out to be about two accesses (per attribute). If an attribute is only used once, this PR is faster; twice, slightly faster or even; three or more times, the previous PR is faster. I decided the complexity of the cache wasn't worth it. There's a cleaned-up version of the caching implementation on my fork if you want to take your own measurements.
(I also didn't test the cache in the free-threaded build; I think the atomics/locking is correct, but I don't really understand how critical sections that can be "suspended" can possibly be safe.)
📚 Documentation preview 📚: https://cpython-previews--139178.org.readthedocs.build/