-
Notifications
You must be signed in to change notification settings - Fork 1
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
Set guidelines to add APIs to the limited C API and to the stable ABI #42
Comments
In Python 3.13, 26 functions were added to Python 3.13 stable ABI: see stable_abi.toml. On my PyList_Extend() PR, @encukou wrote:
So I modified my PR to not add the function the limited C API to Python 3.13. |
We can't predict with reliable accuracy, but we do have decades of software engineering experience between us, which gives us pretty good pattern recognition to lean on. I think one easy guideline is that new API shouldn't be added to the limited API simultaneously (unless it's specifically being added to support the limited API). Give all new API designs a chance to prove themselves before we commit to not changing them for an extended period of time. One extra version won't hurt anyone. |
In fact, I'd prefer that new limited API is vetted by the C-API working group -- preferably in batches, so bigger-picture issues are more apparent. I would like new limited API to be a shining example of best design practices. That's hard to do in PRs that add individual functions, which can be hurried to meet users' needs. |
PyType_FromModuleAndSpec() was added directly to Python 3.12 limited C API. This function was designed with the limited C API in mind, to add a missing feature asked by multiple projects (especially pybind11 if I recall correctly).
Do we have examples of API which were identifed as being problematic after a Python release and that we had to deprecate and/or change quickly after the release? Right now, I fail to remind such example. Even when a non-limited API is broken / bad / a misfit, we still have to support it anyway. Even if it's deprecated, we have to support it for 2 to 3 years anyway. The stable ABI has 62 "ABI only" symbols. It's not like it's a new problem to have to keep functions/variables that we don't want forever :-) Obviously, this function should be as small as possible. By the way, it would be nice to have a process to get rid of them "at some point". It became rare that newly added functions only make sense for non-limited C API. New functions are now designed to avoid implementation details. New functions which rely on implementation details are now added to the internal C API instead. Some new functions are adding "missing features". Examples in Python 3.13:
They are no replacement with the existing API, or very complicated replacements. I would prefer to be able to use these functions in the limited C API as soon as Python 3.13. Some functions are more enhancements to the existing C API. For these ones, well, it's less important to add them immediately to the limited C API.
My plan is to convert most C extensions to the limited C API. If any limited C API addition takes at least 2 years instead of 1, my plan will be 2x slower, and it's already very slow. So well, it hurts me :-) |
Moving to the non-limited C API is not a complicated replacement. Each one of these should prove for its own sake that it's necessary and it is impossible without them to write certain extension modules that ought to be cross-version compatible. IMHO, as soon as you're critically dependent on some of these, you are probably not writing a module that will be binary and semantically compatible with multiple Python versions and implementations. I can be convinced otherwise, but simply saying "they aren't there" isn't enough. By that logic, we should treat the entire CPython API as the limited API, which is clearly not feasible. |
I know. I wrote the PEP -- I used the most rigorous process we had available. And I am talking about changing the status quo. Past examples aren't too relevant -- often they're mistakes to learn from.
Please adjust your plan to work on volunteer timescales. The urgency is entirely artificial. |
Sorry, I'm no longer sure what we are talking about. I'm talking about making the stable ABI usable for non-trivial code base. Today, there are multiple pain points which make the limited C API complicated to use. Look into python/cpython#85283 for examples. I tried to convert some stdlib extensions to the limited C API but I was quickly blocked by the lack of some stupid functions. For example, I'm now working on an API to format a type name in an error message (
Rust extensions only use the stable ABI (PyO3). The new nanobind project to bind C++ with Python also only uses the stable ABI. And these projects are affected by missing features in the stable ABI. Wenzel Jakob of nanobind is active on discuss.python.org, example: https://discuss.python.org/t/use-the-limited-c-api-for-some-of-our-stdlib-c-extensions/32465/56 I'm not sure why, but these devs don't ask for missing features. Maybe because of the tone of replies when they asked for features in the past? Or just because nobdy replied to them in the past? I'm not sure. Well, so far, they used complicated workaround. For example, before PyType_FromModuleAndSpec(), they copied 100 to 200 lines of C code from CPython to fill the lack of this feature. It works, but it's fragile if Python code evolves and they don't update their old copy of the C code. Cython only has a partial support of the limited C API because it's limited and there are missing features. I'm not sure why Cython didn't come with their usecase "we need an API format a type name in the limited C API". Well, so far, we have to be proactive to fill gaps. When do you write "The urgency is entirely artificial": do you mean that the stable ABI is not your agenda, and people must continue to workaround missing features? |
Great, thank you!
Well, I don't think the stable ABI should be driven by the needs of the standard library. It's good to dogfood, sure, but IMO the features for Cython are much more important.
You do have to ask (and then follow up, too).
Yes, I'd rather have them work around missing features for one more year, if that means better API for decades to come. Though i do hope it won't be a year in most cases. |
The reality is that it's more likely to be 3-4 years before anyone really adopts the benefits. Nobody is releasing anything right now with a minimum Python version of 3.12 - I believe most are currently working on moving their minimums to 3.8 (apps seem to target "whatever Ubuntu LTS has by default", whether intentionally or indirectly I'm not sure, but that isn't 3.12 either). So we're already looking at a long-term plan here, and "3-4 years" vs "4-5 years" isn't a huge distinction, particularly when the additions are spread out over multiple releases and all we're really creating is a multi-year process sometime down the track for "does this have all my APIs yet? No? Okay, I'll wait another year then". |
Sometimes, I wish a |
@vstinner slight correction here: PyO3 does use version-specific private APIs, this is for a mixture of legacy reasons and performance. We have an opt-in feature to use the stable ABI. For example If it is useful I can audit the functionality we use from "private" APIs so that we can make a plan how to move PyO3 wholly to the limited C API / stable ABI. I also haven't yet done any testing against the 3.13 alphas with so many functions removed. |
Oh sorry, I didn't know about that. Would you mind to give some examples and explain why the limited C API doesn't fit your these cases? |
@vstinner sure thing, I can audit the PyO3 code for usage of private APIs and compile a list of cases for you. I think it might be slightly off topic for this thread; where would you like me to post it? Might be a couple of weeks before I can find a moment to put the list together. |
As you wish :-)
That's perfectly fine. |
@vstinner @encukou thanks for joining my stream yesterday and helping me take a look at the APIs prefixed with I've just pushed PyO3/pyo3#3762 (comment) which lists what we found on stream and proposals how to make PyO3 not use those private APIs. Fortunately it's not that long. We can continue any discussion there I think, as it's a bit off topic for this particular thread. |
Current limited C API guidelines: https://devguide.python.org/developer-workflow/c-api/index.html#limited-api
Last years, when functions were added to the C API, it was common to also add them directly to the limited C API, and so to the stable ABI.
Later,
Misc/stable_abi.toml
and tests were added: tests fail if an API is added to the limited C API without being added toMisc/stable_abi.toml
. Well, it's just to keepMisc/stable_abi.toml
consistent, it wasn't really a strict rule to make sure that added APIs were designed with the stable ABI in mind.Adding an API to the limited C API requires to put a version with an
#if
, such as:Sadly, there is no automated test to check if the version is tested or not, only manual review can catch it. Recent issue: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED.
When new functions are added to the Python C API, should them be added immediately to the limited C API and the stable ABI, as done currently? Or should it be done on a case by case basis? If yes, based on which criteria?
I don't think that we can predict future ABI issues. The best that we can do is to avoid patterns known to cause ABI issues. For example, exposing structure members in the ABI is risky: any structure change is likely to break the ABI. Another example is that PEP 384 – Defining a Stable ABI asks to not use the
FILE*
type in the limited C API (but file object or file descriptors).The text was updated successfully, but these errors were encountered: