Skip to content

Conversation

@jbosboom
Copy link
Contributor

@jbosboom jbosboom commented Sep 20, 2025

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):

Benchmark statx-nocache namespace
statx-sizemtime-mask-size-mtimens 1.86 us 3.07 us: 1.65x slower
statx-basic-all 2.29 us 4.57 us: 1.99x slower
statx-everything-all 2.61 us 6.34 us: 2.43x slower

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/

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Here is a first review.

@jbosboom
Copy link
Contributor Author

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

for a few functions which have been determined to be more
portable than they had been thought to be.

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.

@vstinner
Copy link
Member

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.

@vstinner
Copy link
Member

We might merge test_posix and test_os into a single test, but that's a different topic.

I created #139322 for that :-)

@jbosboom
Copy link
Contributor Author

jbosboom commented Oct 9, 2025

Do you still plan to merge test_os and test_posix, or have the tests stabilized enough that I should update this PR?

@vstinner
Copy link
Member

vstinner commented Oct 9, 2025

The plan to split test_os is stale for now. You can rebase your PR.

@jbosboom
Copy link
Contributor Author

@gpshead @cmaloney Pinging you because you commented on the previous PR.

@ntninja This PR ended up in a different place than your PR, so I've dropped your attribution. I'd still appreciate any comments you have on this PR.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the change LGTM. There is just one last open question of adding a default value to the mask parameter or not.

Copy link
Contributor

@cmaloney cmaloney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@vstinner
Copy link
Member

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.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but @gpshead asked to convert the flags parameter to a keyword-only parameter.

}
}
else {
pystatx_result_spec.name = "os.statx_result";
Copy link
Member

@vstinner vstinner Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not using os.statx_result name directly in pystatx_result_spec? (line 3505)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. I was just following the pattern of other types created in posixmodule_exec, most of which assign their name here. The tests pass using os.statx_result directly in the spec, so I'll make that change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. That's a strange pattern 😄 I don't know why existing code does that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote #140168 to set type names earlier in posixmodule.c.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@jbosboom
Copy link
Contributor Author

@gpshead asked to convert the flags parameter to a keyword-only parameter.

Yes, I did this.

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).

Done.

@cmaloney
Copy link
Contributor

👍 looking good to me with the kwarg doc change added in.

@vstinner vstinner enabled auto-merge (squash) October 15, 2025 12:13
@vstinner
Copy link
Member

I enabled auto-merge, don't touch anything 😁

@vstinner vstinner merged commit fe9ac7f into python:main Oct 15, 2025
81 of 83 checks passed
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 CentOS9 NoGIL 3.x (tier-1) has failed when building commit fe9ac7f.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1609/builds/3837) and take a look at the build logs.
  4. Check if the failure is related to this commit (fe9ac7f) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

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 logs
remote: 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)

@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot ARM64 Raspbian Debug 3.x (tier-3) has failed when building commit fe9ac7f.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1829/builds/84) and take a look at the build logs.
  4. Check if the failure is related to this commit (fe9ac7f) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

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 logs
remote: 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)

@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot ARM Raspbian 3.x (tier-3) has failed when building commit fe9ac7f.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/424/builds/11822) and take a look at the build logs.
  4. Check if the failure is related to this commit (fe9ac7f) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

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 logs
remote: 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)

@vstinner
Copy link
Member

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

@vstinner
Copy link
Member

@jbosboom: I wrote #140162 workaround to disable stx_atomic_write_unit_max_opt member for now, until a fix (working on CentOS9 and Raspbian) can be designed.

@jbosboom
Copy link
Contributor Author

@vstinner I know what's wrong and how to fix it. I'll have a PR up later today.

@jbosboom
Copy link
Contributor Author

@vstinner @cmaloney Fix PR is #140185.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants