Yes, that is what my backend does. Therefore the version in the tag seems to be repeating existing information. Have I misunderstood?
How does that affect existing abi3
extensions and my ability to create wheels for older Python versions?
Yes, that is what my backend does. Therefore the version in the tag seems to be repeating existing information. Have I misunderstood?
How does that affect existing abi3
extensions and my ability to create wheels for older Python versions?
It is, but it doesn’t mean what you want it to mean. And it demonstrates another reason why abi3.15
won’t work - wheel tags can’t have a .
in them because it clashes with the compressed tag syntax.
Ok, but what does it mean then?
Right, it would need to be spelled abi3_15
. Sorry for the oversight.
abi3.15.abi3.15t
would mean (abi3
or 15
or abi3
or 15t
), which is the same as (abi3
) since the 15
and 15t
wouldn’t match anything.
abi3_15.abi3_15t
would mean (abi3_15
or abi3_15t
).
Yes, they repeat. Requires-Python >= 3.x
version, Py_LIMITED_API
, and the version in the filename an the wheel tag should generally all match, if you’re building a stable ABI wheel.
But, each one has its place:
Py_LIMITED_API
(build-time) and the filename tag (run-time) are for core CPython; they should be useful even if you’re not using PyPA specs/tools.
The wheel tag is needed to get unique filenames for wheels.
That makes Requires-Python
somewhat redundant here, but:
It should have no effect, except abi3
might eventually be incompatible with something like Python 3.25, so you’d need to switch to abi3_15
or later some time in the future.
What “some time” would be is the big question about the length of support windows. How often should people need to rebuild extensions?
I would assume that as soon as we invent any new abi*
at all, we make it compatible between free-threaded and not. That’s the big “problem” with abi3
right now, in that it’s impossible to preserve it and also enable free-threading (see the first few posts in this thread where it was discussed).
As I posted earlier, I think Py_OPAQUE_PYOBJECT
is an unnecessary knob, and we should just rev the API completely.[1] The changes behind Py_OPAQUE_PYOBJECT
are fine, and should be in the next limited API, but no need for an orthogonal flag.
The way to get me on board with that is to make it explicitly about compile-time warnings/failures without producing different binaries. I suspect that’s the case already, but I want it explicit so that it’s clear (for future changes) that it must be safe to mix source units with/without the flag.
That flows nicely into supporting setting the flag immediately before including Python.h
, which takes the build tools out of the equation (unless they want to be there). Users who want to progressively migrate their code can add it to their source code or build scripts, and since it doesn’t matter whether the final binary used the flag or not it doesn’t matter if the build tools ignore it.
I’d also like to have my forward-compatible slots idea considered, since this is the time it makes the most sense. It allows a stable API that can be extended over time without needing to re-version the entire thing or to force older users to fail. It ought to get us out of the place we’ve been for a few versions where 2-3 new “essential” functions are added to the stable ABI each release, making the older release unusable for some users, and avoid permanently leaking abstractions.[2]
To abi4
and Py_LIMITED_API=0x04000000
by preference, decoupled from Python version, but that’s the contentious point you asked to avoid. ↩︎
In that it’s much easier to replace an implementation without breaking the interface (perhaps making the particular operation slow, depending on what changed, but without breaking the entire thing). ↩︎
Yes, if we rev the API completely, Py_OPAQUE_PYOBJECT
will be useless.
But I’m afraid of making these removals block people from using other 3.15 features.
Granted!
Py_OPAQUE_PYOBJECT
does nothing but hide APIs that are incompatible between regular and free-threaded builds.
For the record, the preliminary list is:
struct _object
(making PyObject
opaque)struct PyVarObject
(making PyVarObject
opaque)PyObject_HEAD
_PyObject_EXTRA_INIT
PyObject_HEAD_INIT
PyObject_VAR_HEAD
Py_SIZE
(can be exported as a function instead)Py_SET_TYPE
(can be exported as a function instead)Py_SET_SIZE
(can be exported as a function instead)I think your idea ties in nicely with my forward-compatible slots idea :)
If we do not rev the API completely, these can be added at any point.
This is the bit that made me think Py_OPAQUE_PYOBJECT
had an ABI impact. If there’s no change in output, there’s no need for the build tools or wheel tags to be aware of it.
I’ll let others debate whether it’s even worth doing at that point. I’m inclined to think it’ll be basically free to implement/maintain on top of abi4
, so I’m not so concerned, but I do think it’ll be massively underused and the cost of it should be calculated accordingly.
Mine is the generalisation of yours, yes. Though structs that are not PyObject
s and/or don’t have a PyTypeObject
would need parallel APIs (I do think it makes sense to have those parallel APIs be almost identical though, rather than having inconsistent names and shapes).
They need to put the right tags in wheel & file names. I assume that they’ll want to set the compiler flags as well.
Yes, it should be basically free on top of anything; it’s abi4
that’s the expensive part. Py_OPAQUE_PYOBJECT
is a short list of APIs you can’t use, presented in a way the compiler understands.
The right tag would be cpXY
or abi3
, the same as today? So no change required here?
It should be settable in the source file:
#define Py_OPAQUE_PYOBJECT
#include <Python.h>
...
Being able to set it through anything other than an explicit “add additional defines” feature in a build backend is an exercise for build backends. It’s only a requirement for us to make it happen if we don’t have any other way to set the variable (such as by putting it in the source file).
Or abi3t
.
Yes. You don’t need build tools or wheels at all for Py_OPAQUE_PYOBJECT
.
Some alternatives I can see:
abi3t
makes PyObject
opaque.
Pros:
PyObject
memory layout for free-threaded builds does not have to be frozen. (!)abi3t
would be compatible with non-free-threaded builds. (abi3.abi3t
wheel tags would still be needed for 3.14 and below)Py_OPAQUE_PYOBJECT
define needed: “just” build (with free-threaded Python) to get an extension compatible with 3.15+ & 3.15t+.Cons:
The regular limited API makes PyObject
opaque in 3.15
Pros: as above, plus
abi3t
tag to worry aboutCons: as above, plus:
PyObject
is not made opaque.
Pros:
Cons:
PyObject
memory layout out of stable ABI.Notice that 1 & 2:
#ifdef Py_OPAQUE_PYOBJECT
lines in main
would be rather useful for experimenting with these, even if they’re made obsolete & removed before RC.This sounds pretty doable. The con seems likely to be pretty unavoidable, but as long as people think they’re updating their code to work with free-threading rather than because we just felt like making them, I don’t think it’ll be too much of a problem.
Even if we did this, we can’t change the layout of PyObject
until whenever we decide that limited API modules built with 3.14 are no longer supported. There isn’t an agreed upon definition of this deprecation timeline (that I’m aware of), so it seems likely that we’ll need a hard break migration at some point before it expires anyway. May as well be planning for that (and supporting it in parallel as much as we can) rather than trying to patch over design decisions from the past.