gh-122931: Allow stable API extensions to include a multiarch tuple in the filename#122917
gh-122931: Allow stable API extensions to include a multiarch tuple in the filename#122917stefanor wants to merge 6 commits into
Conversation
5e380a1 to
7185a58
Compare
…ple in the filename This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, the same way (non-stable ABI) regular extensions can. It is listed below the current .abi3 suffix because setuptools will select the first suffix containing .abi3, as the target filename. We do this to protect older Python versions predating this patch.
7185a58 to
2955027
Compare
…ple in the filename This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, the same way (non-stable ABI) regular extensions can. It is listed below the current .abi3 suffix because setuptools will select the first suffix containing .abi3, as the target filename. We do this to protect older Python versions predating this patch.
2955027 to
c5b58a4
Compare
vstinner
left a comment
There was a problem hiding this comment.
You should document this change in https://docs.python.org/dev/whatsnew/3.14.html (Doc/whatsnew/3.14.rst). I expect that you would elaborate a bit on the usage, how to use the feature, why it's needed, etc.
Done. FWIW, in Debian we plan to backport this to 3.13 too. |
|
I'm only aware of Debian who uses "multiarch". Do other operating systems also use it? Maybe Debian variants, Ubuntu, and Ubuntu variants? This change will slow down any "import module". I don't recall if there is a cache for that or not. |
All Debian derivatives, yes. They typically don't deviate very much, when it comes to plumbing. |
What do you want to do about that? Is there a benchmark you'd like to see results for? I see a table of 5 entries (including NULL) increasing to 6. That is one extra item to search, when:
|
Example of command: strace output: Internally, when Python imports the If we add a new entry to the import suffix, it will add one fstat() syscall per import (at least, to import a package). We should consider the impact on performance if we decide to add this feature. |
FWIW: macOS, iOS and Android all use However, on iOS and Android, there's limited need to keep binary artefacts in the same folder, as any given executable can only have a single architecture's executables. In addition, in the case of iOS, the binaries need to be migrated to the Frameworks folder and named as Frameworks, so any "side-by-side" benefits would be lost. Any "other platform" executables need to be stripped out as part of the build process, at which point there's no naming conflict. macOS uses the multiarch config value, but the value is always "darwin", and the universal binary format exists to support multiple architectures in a single binary file. I guess it might be useful to be able to have x86_64 and ARM64 binaries side-by-side... but there's probably only a year or two left in the official supported life of x86_64, so I don't think adding this feature will ultimately be that helpful for the macOS use case. |
|
@stefanor: Did you consider to maintain this change as a downstream-only patch in Debian? If you would like to make it upstream, I would suggest making it optional, disabled by default, and add a configure option to enable it. That's how I added some Fedora specific changes, such as: |
|
Here are some benchmarks: There is no discernable performance difference in minimal interpreter startup import time
import sys
from subprocess import check_call
t1 = time.perf_counter()
for i in range(1000):
check_call([sys.executable, "-c", ""])
t2 = time.perf_counter()
print((t2-t1) / 1000)Looking at strace, I see only a single extra syscall. We can manufacture an import-intensive benchmark: import pkgutil
import time
t1 = time.perf_counter()
for module in pkgutil.walk_packages(onerror=lambda pkg: None):
if module.name.startswith("test."):
continue
if module.name.endswith(".__main__"):
continue
if module.name in {"antigravity", "idlelib.idle", "this", "zen"}:
continue
try:
__import__(module)
except Exception:
pass
t2 = time.perf_counter()
print((t2 - t1))Analysing this, I see 116 stat syscalls on |
Also, note that this is used on all linux platforms. The default for (non-stable ABI) extensions is to include the multiarch tuple in the extension filename. Pick a random binary wheel built in manylinux, and you'll see them.
I would be happy to do that. Although I prefer not carrying downstream-only patches long-term if possible. Look at the mess around Without this MR, there's no real point in supporting the multiarch tags in the non-stable ABI extensions. Either we get the benefit from doing it everywhere, or you say you don't need to support the feature, and we rip it out everywhere. We've got half a feature at the moment. Would you prefer it to all be behind a config argument?
These are not entirely Fedora-specific. And that's a good argument for always trying to upstream.
In Debian we use those paths for our cross-compilers. I imagine there is a scenario where it's useful to have Python headers in there.
We're a happy customer of this too, It meant one less patch to carry. |
|
@erlend-aasland @encukou: Would you mind to have a look at this issue? What do you think? Should it become the default behavior, or should it be a configure option? |
|
IMO, the most important thing here is to keep this in mind for |
Yeah, that sounds sensible.
Is that expected? We've been on abi3 for quite a while. |
If we want that for the stable ABI, do we want to do the same thing for the regular ABI? It would probably affect a lot of build systems that assume they can name their output |
Yes, over a decade. abi3 showing its age, and since it's incompatible free-threading we'll need a new alternative soon.
I don't see much reason to deprecate and remove that, so that we don't break the build systems you mention. (Note that even abi3/abi4 extensions will work with a bare |
I'm going to include it in our 3.13.0 upload. |
|
🔔 (I'd like to remind people to please consider this) |
Noted that you wouldn't recommand anybody else implement this interface. Being closer to it, I have a much more positive view. If you look at the problems it's solving, I really don't see any other obvious way to go about it that cause less friction. It massively reduces the amount of duplication and churn we have to do in the distro. It should also be a useful tool for anybody who wants to ship something for multiple Python releases and/or architectures. How would you like to update the NEWS messaging to represent your view here? If this is all something that Python wants to deprecate, we should design a replacement for it. We just had a packaging summit at PyCon. If that was something we should have discussed, I wish I'd known that. |
0117088 to
7b071c2
Compare
I think that entry is fine, it's just a factual description of the technical change in this PR. I don't think it needs to touch on anything bigger-picture.
I certainly don't speak for Python (I'm not even a core dev), but: I don't think anyone wants to push for that. It's fine to exist.
The key thing that seems needed is integration testing for interleaved installs CPython, so that the next time any requirements get captured in time, rather than afterwards. That came up on Discourse before, can't find the link right now but it was recent so I guess you remember? |
This change adds 2 more stat() syscalls per Python import. Example with
Example with
My position didn't change. IMO it's a bad idea to add syscalls on all Unix platforms whereas only Debian, Ubuntu and variants need this change. As I wrote previously, I would prefer adding a configure option and use this option on Debian/Ubuntu. We are working hard to reduce the Python startup time (lazy import is the latest major work on that). Adding "useless" syscalls sounds counter productive to me. I would be perfectly fine with a configure option, so Debian/Ubuntu no longer have to maintain a patch downstream. |
|
@vstinner: There are a few other components to this.
|
|
What are Python only supports |
|
That is, of course, what I meant. It was proposed earlier in this MR, that we defer the change for |
|
As I mentioned the historical background for adding platform triplet information in the above linked ticket, I also happened to notice there was quite some discussion about the benefits. The background: #67169 I think that these are useful considerations that are still just as relevant today as they were in the past. And we are already past the point of no return in committing to an additional syscall, I think (given we are settled on looking for abi3t.so), which means there isn't even a performance problem to be had in switching from one syscall to one syscall. So, what precisely is the objection to at least handling this? @vstinner, can you confirm you have no objection? Previously you said your objection was about adding new syscalls, but when asked specifically about abi3t you took a detour into arguing about what the final name of abi3t is, and never really replied after that. The exact same thing happened in October of 2025, when you were asked about getting this right at least for the next ABI and instead of answering the question you said "right now there are no plans for an abi4": #122917 (comment) abi4 is here. It is called abi3t. Debating which name to use is strange arguing over semantics. Everyone agrees it is a new ABI, with a new ABI soname, and it's the one after What name shall we look it up using? (@stefanor, please proactively do so anyway. I often find that people are much more resistant to "will you accept it if I XYZ" than "I did XYZ, will you accept it now". It's weird but it does help, so hopefully that will be enough here.) |
If this is still changed, it really should be done yesterday, and it might arguably already be too late to do the "or" rather than "look for It's unlikely any wheels were already uploaded to PyPI, but it's not impossible - those would have to be yanked or hard-deleted (with lock files, yanking may not be enough). |
Python 3.15 looks for I'm not aware of a plan to look for UPDATE: The |
|
I'm not sure that this pull request is the best place to gather feedback on supporting another filename tag for C extensions. Someone should open a dedicated topic on discuss.python.org.
Changing filenames impacts many things, not just How to support Python 3.14 and older which don't support it? Should the two filenames be uploaded to PyPI for each release? Such question should be answered first. |
@eli-schwartz: Absolutely, I can do that. Commits are cheap :)
Yeah, that's my feeling too. I expected that the people involved in this PR discussion would help to make this happen for abi3t, from the starting gate.
@rgommers: Agreed. But I've been up against a hard rock here. The biggest objection to this change has been performance (of supporting both names), and that's also what we'd get from just doing this right for abi3t from the start.
@vstinner: I did the analysis of tool support in #122931 (comment) Yes, some work needs to happen, but I'm willing to drive it.
I will adjust this PR, on Eli's advice, to target abi3t only. 3.14 doesn't support |
That list still seems accurate. |
f88b12a to
f84ef65
Compare
…ple in the filename This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, the same way (non-stable ABI) regular extensions can. It is listed below the current .abi3 suffix because setuptools will select the first suffix containing .abi3, as the target filename. We do this to protect older Python versions predating this patch.
f84ef65 to
67e2548
Compare
|
Rebased onto |
I'm certain that build backends will be happy to do new releases in order to align with the technical decisions of an evolving situation in unstable, unreleased cpython. This isn't a barrier.
If they were uploaded then they're already broken by default since the ABI won't be frozen until rc1. Given cpython refuses to limit itself out of concern that wheels with wrong ABI / crashing extensions will have already been uploaded, it would be heavily inconsistent to limit itself out of concern that wheels with undetected / wrong filename extensions might have been uploaded to PyPI. They're both just as "naughty" / unsupported, but the breakage if someone did the naughty thing is far worse for wrong ABI, and that's already a routine event in cpython development. |
Again, that's why I think that a discussion on discuss.python.org would be better (to coordinate). Maybe in the Packaging category. |



This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, the same way (non-stable ABI) regular extensions can.
It is listed below the current .abi3 suffix because setuptools will select the first suffix containing .abi3, as the target filename. We do this to protect older Python versions predating this patch.