-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supporting multiple ABI versions at once #39
Comments
This is the kind of compatibility story that we never solved for the Python 2-to-3 transition. It set us back by a decade. So it would behoove us to do better: even though the scope here is smaller (just stable-abi-using extensions), the size of the affected ecosystem is likely even larger than it was then. I suspect that this can only be solved properly by constraining ABI 2.0 in some way, or at least by some clever hacks. For example, let's say we want to change But now we're stuck with the ugly Still, for readers of extension module source code it's a bit confusing that sometimes At this point it's a matter of choice, though -- we certainly can design an ABI 2.0 in such a way that it can coexist with ABI 1.0 at runtime, as long as the function names seen at the linker level are different. (Functions whose semantics are unchanged don't need to be different, though they could be for consistency. Another choice.) |
I believe that the Another class of compatibility issues related to ABI stability is stability of some implicit contracts such as GIL vs. per interpreter GIL vs no GIL. For that one we should allow extensions to communicate to the runtime what expectations they have (i.e., declare some flags/options). This must be designed in a way that allows binary compatible evolution.
That's one option. Another approach was taken by HPy, where there is no linking, the only "stable forever" contract is that the extension must expose symbols For now, it creates an If another extension is loaded that requires different If we decided to ditch the Of course, another issue is how to support old/legacy extensions, which make some assumptions such as that there is GIL, and at the same time support new extensions/code that want to run with per interpreter GIL. Inherently we need a way to at least emulate the GIL for the old extensions while not actually doing it. This problem is orthogonal to how the API/ABI looks like. What can help with the HPy design is that the different There is a blog post about the HPy design with more details. Note that it was written before we introduced the https://medium.com/graalvm/hpy-binary-compatibility-and-api-evolution-with-kiwisolver-7f7a811ef7f9 |
Let's just accept that and move on? For the other issues: IMO, the new stable API should only contain functions (and types needed for their arguments). It should never allow you to get any complex type back (so if you create a module using HPy's approach -- versioning the whole API -- seems good if you need to support alternative implementations, but IMO CPython would be better off versioning individual functions. |
I think there can be some situations where versioning of individual functions may not be sufficient as explained in my previous comment. On the top of that, with the HPy approach the extension doesn't even get any access at all to different API/ABI versions, so it cannot accidentally/intentionally (people can be creative sometimes) mix incompatible versions. |
We already do that; for example |
I proposed PyList_GetItemRef() :-) Or just use PySequence_GetItem() which already exists and returns a new strong reference.
Shortly after implementing PEP 445 which added the new |
By the way, I never understood why |
I vaguely recall that some people tried running Python 2 and Python 3 in the same process. Honestly, that would be cool if it worked, especially if it would be easy to share objects betwen the two "spaces". If you imagine doing the same with thread-safe ("nogil") C extensions with old (not thread-safe) C extensions, it would be cool to be able to use the two at the same time. But the ABI may be very different and so incompatible. Look also at PyObject.ob_refcnt changes caused by immortal objects (PEP 683). The ABI differences between recent and old Python is only growing. It's very appealing to imagine that loading an old and a new ABI in the same process would work. The problem is that the Python ABI is badly designed in term in backward and forward compatibility. Honestly, IMO even the stable ABI is still leaking too many implementation details (like PyObject members). The Linux glibc uses symbol versionning which is pretty cool. It helps to change the ABI and still support running old unmodified binaries. It relies on Linux ELF symbol versionning. If we want something similar in a portable way, we can just change the symbol name. Like: PyDict_GetItem (version 1), PyDict_GetItem2 (version 2), etc. The ABI and the API can use different names. For example, the API can use |
FTR, I intended capi-workgroup/api-revolution#4 to be able to handle this kind of multiple ABIs - rather than adding new symbols, we add a new set of slots. New code "gets" those if it can, or else uses the old ones (which might be new implementations as well). |
In order to be able to evolve the API, but keep ABI stability, it would be useful if one Python version could support multiple ABI versions at once. How would it look like:
The text was updated successfully, but these errors were encountered: