GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-83714: Use statx on Linux 4.11 and later in os.stat by jbosboom · Pull Request #136334 · python/cpython Conversation
|
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Please update the title to use the gh issue number. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Some docs suggestions / CPython sphinx has special tags for a couple of the things; haven't looked at main implementation a lot yet
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
!buildbot .Android. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
🤖 New build scheduled with the buildbot fleet by @gpshead for commit b0e8276 🤖 Results will be shown at: https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F136334%2Fmerge The command will test the builders whose names match following regular expression: The builders matched are:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
FYI, we recently added Android to the GitHub Actions CI, so there's no longer much need to invoke the buildbots manually. The exception is if a PR has something which might be specific to Android aarch64, because GitHub Actions can't run the tests on that platform yet, although it will still perform a complete build. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Looking at the failures, the Android buildbots didn't run the most up-to-date code from this PR. Looking at the "Build Properties" tab, the "revision" is "b0e827681f45588ce67cd0924b3c320bb64351ff" (the latest), but "got_revision" is "a387390693c4f4ff793717a91a5137b7f608f9e4", a merge commit attributed to me but that I don't remember making of d53650a (the commit just before I added the configure check) with main. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Both the buildbots and GitHub Actions don't actually test the head of the PR branch, but a speculative commit showing what you would get if you merged the PR into main. Because of a merge conflict, it's not possible to merge the PR into main at the moment. It looks like GitHub Actions deals with this by not running any tests at all, while the buildbots test the last commit before the conflict. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I dislike this approach: modifying Python os.stat() to use statx. I would prefer to expose C statx() as Python os.statx() instead. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
… raw kwarg We no longer return None for invalid attributes for the benefit of drop-in replacement of os.stat when not all basic stats are required or sync=False is acceptable. Such code was already accepting faked values from os.stat, so can also accept faked values from os.statx. Remove the raw kwarg as it would have no effect.
These members were removed from os.stat_result in a previous commit.
|
I've marked this ready for review. @vstinner I'd also appreciate a review from you; GitHub isn't showing me the interface to formally request one. This PR has changed quite a bit from the first commit, and as it did so, I wrote quite a few words in the issue #83714 that may be informative (probably best to read from the bottom up). |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Reviewed the pieces I have reasonable knowledge of; don't have enough experience with macros and defining new structs to go in more detail through those pieces. For first contributions to CPython really impressive in getting things to the CPython style (docs, C code style, etc)
High level:
os.stat change will need (on newer + older kernels). Just adding statx doesn't need as much performance validation
pyperf microbenchmark that shows stat didn't get slowerstatx will probably make sense to add to pathlib.Path for easy access like stat is (but that should probably be another PR with the same issue)With the thoroughness you're doing definitely feels like will get to the confidence to change os.stat to point to os.statx. Again; really impressive for a first CPython contributions.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| .. function:: statx(path, mask, *, dir_fd=None, follow_symlinks=True, sync=None) | ||
|
|
||
| Get the status of a file or file descriptor by performing a :c:func:`statx` |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should be able to make the missing c func / struct pieces stop erroring by adding to:
Line 121 in 4e00e25
| nitpick_ignore = [ |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
| Get the status of a file or file descriptor by performing a :c:func:`statx` | |
| Get the status of a file or file descriptor by performing a :c:func:`!statx` |
This will also work.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Running make html and make check as directed by the devguide, I don't see any warnings.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| actually retrieved (which may differ from *mask*). | ||
|
|
||
| The optional parameter *sync* controls the freshness of the returned | ||
| information. ``sync=True`` requests that the kernel return up-to-date |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should document the value of None which is the default; may make sense to just make it always True/False for now
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| self.assertAlmostEqual(floaty, nanosecondy, delta=2) | ||
|
|
||
| # Ensure both birthtime and birthtime_ns roughly agree, if present | ||
| time_attributes = 'st_atime st_mtime st_ctime'.split() |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
nit: why not just make a three element list here? (I see the old code did this, but seems reasonable to just update)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| def check_statx_attributes(self, fname): | ||
| maximal_mask = 0 | ||
| for name in dir(os): | ||
| if name.startswith('STATX_'): |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
feels like there should be a better way to group / gather these (maybe have them all live as constants in a Python class that acts as a namespace?)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Similar constants for other os functions are also dumped in the module namespace. That doesn't mean we can't do something different, but we'd be setting somewhat of a precedent.
(It would be nice to have an IntFlag enum class that can report which members are valid given a bitmask, but adding an import dependency to os doesn't seem like a good idea.)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'm fairly strongly against "find everything that starts with STATX_ in os" as that will just get slower as os gets larger.
Can be just a manually maintained list for now; would be nice to do something more namespace like (but also don't want to add a new CPython standard here).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| for sync in (False, True): | ||
| with self.subTest(sync=sync): | ||
| os.statx(self.fname, os.STATX_BASIC_STATS, sync=sync) | ||
|
|
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Will need more test cases for different ways of constructing/using statx (path_fd, dir_fd, PathLike)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should the dir_fd tests go in here in test_os.py or in TestPosixDirFd in test_posix.py?
Should all the statx tests be in test_posix.py? The comment at the top of test_os.py says it's for "a few functions which have been determined to be more portable than they had been thought to be", and statx is not.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think in test_os works; don't need to test that "do the dir_fd helpers use handle every case right", want to just make sure "if someone deleted the dir_fd handling code; a test would fail"
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
test_posix seems to be around https://docs.python.org/3/library/posix.html; I don't have experience / knowledge around that so not sure
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| int flags = AT_NO_AUTOMOUNT; | ||
| flags |= follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; | ||
|
|
||
| Py_BEGIN_ALLOW_THREADS |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't see a reason not to start allowing threads a bit earlier; aren't touching the interpreter in the lines just before this
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| typedef struct { | ||
| PyObject_HEAD | ||
| struct statx stx; | ||
| #if STATX_RESULT_CACHE_SLOTS > 0 |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
isn't this always > 0? (the constant 17)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
It's defined this way to allow reviewers and future maintainers to more easily experiment with not having a cache (because member arrays can't be empty in C). On reflection, it doesn't help much, because the no-cache configuration would change most of the getset defs to members for faster access, and after that, commenting out the member is trivial.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| int result; | ||
| /* Future bits may refer to members beyond the current size of struct | ||
| statx, so we need to mask them off to prevent memory corruption. */ | ||
| mask &= _Py_STATX_KNOWN; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should this error if the mask was changed by this code?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe. We'd only adjust the mask for Python code that specified the mask numerically instead of using the os.STATX_* constants. That's probably only code that wants to use members Python 3.16 knows about but Python 3.15 doesn't. os.statx_result doesn't provide Python with an interface to access members the interpreter doesn't know about, so while code could use the numeric value of STATX_FOO, accessing stx_foo would raise AttributeError. And that's how current code copes with os.stat_result's members varying across versions. But I'm open to arguments that failing fast is better and version-crossing code should instead change their mask based on the constants available.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| #endif /* HAVE_EVENTFD && EFD_CLOEXEC */ | ||
|
|
||
| #ifdef HAVE_STATX | ||
| if (PyModule_AddIntMacro(m, STATX_TYPE)) return -1; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
nit: Any reason not to alphabetize this list?
I like having either a comment why not alphabetical or to alphabetize for easier later code reading "is this member in the list" when trying to add more
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The order matches the definitions in /usr/include/linux/stat.h. When new bits are defined, they're added at the end (there are no gaps in the definitions). Alphabetizing would make things harder, not easier.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I don't recommend this. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
👍 / agree with you, meant to say |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
glibc doesn't remember that statx previously failed with ENOSYS, so on old kernels, glibc's emulation of statx via stat has the overhead of a failed syscall. Only call the statx wrapper under the same conditions that we make os.statx available. statx_works was always a global C variable, but now its scope is the file instead of just posix_do_stat. Whether statx works is a process-global property, so using a global C variable is appropriate. Previously access to statx_works was unsynchronized, following the pattern of dup3_works. Now we rely on posixmodule_exec happening-before any thread can call os.stat.
Also two minor fixes to statx tests in test_os.py.
|
Some benchmark numbers comparing
The 'PR nobtime' row replaces the last I don't have a VM running a pre-statx kernel handy, but I did find the The following numbers are from under
We're maybe slightly slower, and this time not due to an extra float and int, but if you look at the histograms in the gist, there are more outliers here. It's hard to conclude much. pyperformance startup benchmark, main vs this PR: main vs PR nobtime: PR nobtime vs PR: This PR doesn't change any of the code in fileutils.c to use statx, so if we're slower, it's because of Python main vs this PR running under |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
As I wrote previously, I would prefer to leave I support the idea of adding a new I suggest creating a new PR which only adds a new |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Hmm. In 2024 a user asked for an update on the issue and you replied that ntninja's PR needed to be updated. That PR changed
That's a non sequitur. The reason I am pushing so hard for
And this PR does in fact add a |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Victor and I synced on the above at our ongoing core team sprint yesterday, I'm in agreement: Lets start with only While that could be done in this PR you may find it cleaner for review purposes to make a new PR focused on just that.
I really appreciate that you're focusing on the performance of this. I'm interested to see what you find! Long term, we envision there becoming a higher level API than the os module as a path stat interface. It could use os.stat, os.statx, and other APIs under the hood. But os.stat itself should not become such an API. I realize this is proposing that we ultimately create a new higher level API for stat. But things like We should track a new higher level API with its own issue. That may wind up becoming a PEP-style discussion as it'd be a new high level API - to be determined. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@gpshead @vstinner While I agree pathlib should provide a nicer interface for stat, you are more optimistic than I am as to how soon that nicer interface will arrive. I guess as core team members you have more control over when it happens. :) I created a new PR #139178. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Pathlib updates for stat attributes are currently being discussed -- https://discuss.python.org/t/expose-file-size-mode-etc-from-pathlib-path-info/103828 questions/criticism/ideas welcome :). |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.