PEP 689: Rewrite, with PyUnstable_ prefix (GH-2847)
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
13c89dc8cb
commit
e707124176
323
pep-0689.rst
323
pep-0689.rst
|
@ -2,22 +2,14 @@ PEP: 689
|
|||
Title: Unstable C API tier
|
||||
Author: Petr Viktorin <encukou@gmail.com>
|
||||
Discussions-To: https://mail.python.org/archives/list/python-dev@python.org/thread/PQXSP7E2B6KNXTJ2AERWMKKX42YP5D6O/
|
||||
Status: Deferred
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Requires: 523
|
||||
Created: 22-Apr-2022
|
||||
Python-Version: 3.12
|
||||
Post-History: `27-Apr-2022 <https://mail.python.org/archives/list/python-dev@python.org/thread/PQXSP7E2B6KNXTJ2AERWMKKX42YP5D6O/>`__,
|
||||
|
||||
|
||||
Deferral note
|
||||
===============
|
||||
|
||||
This PEP was accepted provided that function names in the unstable API start
|
||||
with leading underscores. Unfortunately, this uncovered wider disagreement
|
||||
about the meaning of leading underscores. The PEP's author is not comfortable
|
||||
pushing the PEP forward before we agree on what a leading underscore means.
|
||||
`25-Aug-2022 <https://discuss.python.org/t/c-api-what-should-the-leading-underscore-py-mean/18486>`__,
|
||||
|
||||
|
||||
Abstract
|
||||
|
@ -28,183 +20,228 @@ meaning that they will not change in patch (bugfix/security) releases,
|
|||
but may change between minor releases (e.g. between 3.11 and 3.12) without
|
||||
deprecation warnings.
|
||||
|
||||
Any C API with a leading underscore is designated *internal*, meaning that it
|
||||
may change or disappear without any notice.
|
||||
|
||||
|
||||
Motivation & Rationale
|
||||
======================
|
||||
|
||||
The Python C-API is currently divided into `three tiers <https://devguide.python.org/c-api/>`__:
|
||||
Unstable C API tier
|
||||
-------------------
|
||||
|
||||
The Python C-API is currently divided into `three stability tiers <https://devguide.python.org/developer-workflow/c-api/index.html>`__:
|
||||
|
||||
- Limited API, with high compatibility expectations
|
||||
- Public API, which follows the :pep:`backwards compatibility policy
|
||||
<387>`, and requires deprecation warnings before changes
|
||||
- Private (internal) API, which can change at any time.
|
||||
- Internal (private) API, which can change at any time.
|
||||
|
||||
Tools requring access to CPython internals (e.g. advanced
|
||||
debuggers and JIT compilers) are often built for minor series releases
|
||||
of CPython, and assume that the C-API internals used do not change
|
||||
in patch releases. To support these tools, we need a tier between the
|
||||
Public and Private C-API, with guarantees on stability throughout
|
||||
the minor-series release.
|
||||
the minor-series release: the proposed *Unstable tier*.
|
||||
|
||||
Some functions, like ``PyCode_New()``, are documented as unstable
|
||||
(“Calling [it] directly can bind you to a precise Python version”),
|
||||
and also often change in practice.
|
||||
The unstable tier should make their status obvious even to people who don't
|
||||
read the docs carefully enough, making them hard to use accidentally.
|
||||
|
||||
|
||||
Setting Stability Expectations
|
||||
------------------------------
|
||||
Reserving leading underscores for Private API
|
||||
---------------------------------------------
|
||||
|
||||
Currently, there are no guarantees for the internal API -- that is, anything
|
||||
that requires ``Py_BUILD_CORE`` or is named with a leading underscore.
|
||||
This API can change without warning at any time, and code that uses it
|
||||
is pinned to a specific build of Python.
|
||||
Currently, CPython developers don't agree on the exact meaning of a leading
|
||||
underscore in API names.
|
||||
It is used to mean two different things:
|
||||
|
||||
However, in practice, even the internal API usually happens to be stable
|
||||
in patch releases:
|
||||
- API that may change between minor releases, as in the Unstable tier proposed
|
||||
here (e.g. functions introduced in :pep:`523`).
|
||||
- API that is *private* and should not be used outside of CPython at all
|
||||
(e.g. because it may change without notice, or it relies on undocumented
|
||||
assumptions that non-CPython code cannot guarantee).
|
||||
|
||||
- Some CPython core developers take this as an an unwritten rule.
|
||||
- Patch releases only contain bugfixes, which are unlikely to
|
||||
change the API.
|
||||
The unclear meaning makes the underscore less useful than it could be.
|
||||
If it only marked *private* API, CPython developers could change underscored
|
||||
functions, or remove unused ones, without researching how they're
|
||||
documented or used outside CPython.
|
||||
|
||||
Unstable API will make the stability expectations more explicit.
|
||||
|
||||
It will also hopefully encourage existing users of the private API to
|
||||
reach out to python-dev, so we can expose, standardize and test an API
|
||||
for some of their use cases.
|
||||
With the introduction of an unstable tier, we can clarify the meaning
|
||||
of the leading underscore, eventually making it OK to skip that research.
|
||||
|
||||
|
||||
Reserving underscores for Private API
|
||||
-------------------------------------
|
||||
Not breaking code unnecessarily
|
||||
-------------------------------
|
||||
|
||||
:pep:`523` introduced functions for use by debuggers and JIT compilers,
|
||||
which are stable only across minor releases.
|
||||
The functions names have leading underscores to suggest their limited
|
||||
stability.
|
||||
|
||||
However, leading underscores usually mark *fully private* API.
|
||||
CPython developers familiar with the “underscore means internal”
|
||||
convention are unlikely to check if underscored functions they are
|
||||
changing are documented and used outside CPython itself.
|
||||
|
||||
This proposal brings us a bit closer to reserving underscores
|
||||
only for truly internal, private, hands-off API.
|
||||
|
||||
|
||||
Warning about API that is changed often
|
||||
---------------------------------------
|
||||
|
||||
The ``PyCode_New()`` family is an example of functions that are
|
||||
documented as unstable (“Calling [it] directly can bind you to a precise
|
||||
Python version”), and also often change in practice.
|
||||
|
||||
Moving it to the unstable tier will make its status obvious even
|
||||
to people who don't read the docs carefully enough, and will make it
|
||||
hard to use accidentally.
|
||||
|
||||
|
||||
Changes during the Beta period
|
||||
------------------------------
|
||||
|
||||
Since the API itself can change continuously up until Beta 1 (feature freeze)
|
||||
of a minor version, major users of this API are unlikely to test
|
||||
Alpha releases and provide feedback.
|
||||
It is very difficult to determine what needs to be exposed as unstable.
|
||||
|
||||
Additions to the unstable tier will count as *stabilization*,
|
||||
and will be allowed up to Release Candidate 1.
|
||||
This PEP specifies that API should be renamed so that the
|
||||
public/unstable/internal stability tier is expressed in function names.
|
||||
Whenever this happens, the old name should continue to be available until
|
||||
an incompatible change is made (i.e. until call sites need to be updated
|
||||
anyway).
|
||||
In other words, just changing tiers shouldn't break users' code.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
Several functions and types (“APIs”) will be moved to a new *unstable* tier.
|
||||
The C API is divided by stability expectations into `three “sections” <https://devguide.python.org/developer-workflow/c-api/index.html>`__
|
||||
(internal, public, and limited).
|
||||
We'll now call these *stability tiers*, or *tiers* for short.
|
||||
|
||||
They will be expected to stay stable across patch releases,
|
||||
but may change or be removed without warning in minor releases (3.x.0),
|
||||
including Alpha and Beta releases of 3.x.0.
|
||||
An *Unstable tier* will be added.
|
||||
|
||||
When they change significantly, code that uses them should no longer compile
|
||||
(e.g. arguments should be added/removed, or a function should be renamed,
|
||||
but the semantic meaning of an argument should not change).
|
||||
APIs (functions, types, etc.) in this tier will named with the ``PyUnstable_``
|
||||
prefix, with no leading underscore.
|
||||
|
||||
Their definitions will be moved to a new directory, ``Include/unstable/``,
|
||||
and will be included from ``Python.h``.
|
||||
Internally, they will be declared in headers in a new directory,
|
||||
``Include/unstable/``.
|
||||
Users should include ``Python.h`` rather than using these headers directly.
|
||||
|
||||
From Python 3.12 on, these APIs will only be usable when the
|
||||
These APIs will only be declared when the
|
||||
``Py_USING_UNSTABLE_API`` macro is defined.
|
||||
CPython will only define the macro for building CPython itself
|
||||
CPython will define the macro for building CPython itself
|
||||
(``Py_BUILD_CORE``).
|
||||
|
||||
To make transition to unstable API easier,
|
||||
in Python 3.11 the APIs will be available without ``Py_USING_UNSTABLE_API``
|
||||
defined. In this case, using them will generate a deprecation warning on
|
||||
compilers that support ``Py_DEPRECATED``.
|
||||
Several rules for dealing with the unstable tier will be introduced:
|
||||
|
||||
A similar deprecation period will be used when making more APIs unstable
|
||||
in the future:
|
||||
- Unstable API should have no backwards-incompatible
|
||||
changes across patch releases, but may change or be removed in minor
|
||||
releases (3.x.0, including Alpha and Beta releases of 3.x.0).
|
||||
Such changes must be documented and mentioned in the What's New document.
|
||||
|
||||
- When moving from public API, the deprecation period should follow Python's
|
||||
backwards compatibility policy (currently, it should last at least
|
||||
two releases).
|
||||
- When moving from public API that is documented as unstable,
|
||||
the deprecation period can only last one release.
|
||||
- When moving from private API or adding new API, no deprecation period
|
||||
is necessary.
|
||||
- Backwards-incompatible changes to these APIs should be made so that
|
||||
code that uses them will need to be updated to compile with
|
||||
the new version (e.g. arguments should be added/removed, or a function should
|
||||
be renamed, but the semantic meaning of an argument should not change).
|
||||
|
||||
Leading underscores will be removed from the names of the moved APIs.
|
||||
The old underscored name of a renamed API will be available (as an alias
|
||||
using ``#define``) at least until that API changes.
|
||||
- To move an API from the public tier to the unstable tier, it should be
|
||||
exposed under the new ``PyUnstable_*`` name and the definition should be
|
||||
guarded with ``Py_USING_UNSTABLE_API``.
|
||||
|
||||
The unstable C-API tier and ``Py_USING_UNSTABLE_API`` will be documented,
|
||||
and documentation of each unstable API will be updated.
|
||||
The old name should be deprecated (e.g. with ``Py_DEPRECATED``), but
|
||||
continue to be available until an incompatible change is made to the API.
|
||||
Per Python's backwards compatibility policy (:pep:`387`), this deprecation
|
||||
needs to last *at least* two releases (without an SC exceptions).
|
||||
But it can also last indefinitely -- for example, if :pep:`590`'s
|
||||
:pep:`“provisional” <590#finalizing-the-api>`
|
||||
``_PyObject_Vectorcall`` was added today, it would be initially named
|
||||
``PyUnstable_Object_Vectorcall`` and there would be no plan to ever remove
|
||||
this name.
|
||||
|
||||
In the following cases, an incompatible change (and thus removing the
|
||||
deprecated name) is allowed without an SC exeption, as if the function was
|
||||
already part of the Unstable tier:
|
||||
|
||||
- Any API introduced before Python 3.12 that is *documented* to be less
|
||||
stable than default.
|
||||
- Any API introduced before Python 3.12 that was named with a leading
|
||||
underscore.
|
||||
|
||||
For examples, see the :ref:`initial unstaple API <pep689-initial-list>`
|
||||
specified in this PEP.
|
||||
|
||||
- To move an *internal* API to the unstable tier, it should be
|
||||
exposed under the new ``PyUnstable_*`` name and the definition should be
|
||||
guarded with ``Py_USING_UNSTABLE_API``.
|
||||
|
||||
If the old name is documented, or widely used externally,
|
||||
it should continue to be available until an
|
||||
incompatible change is made (and call sites need to be updated).
|
||||
It should start raising deprecation warnings.
|
||||
|
||||
- To move an API from the unstable tier to the public tier, it should be
|
||||
exposed without the ``PyUnstable_*`` prefix.
|
||||
|
||||
The old name should remain available, possibly without requiring
|
||||
``Py_USING_UNSTABLE_API``, until the first incompatible change is made
|
||||
otr the API is removed.
|
||||
|
||||
- Adding new unstable API *for existing features* is allowed even after
|
||||
the feature freeze, up until the first Release Candidate.
|
||||
Consensus on Core Development Discourse or ``capi-sig`` is needed in the
|
||||
Beta period.
|
||||
|
||||
These rules will be documented in the `devguide <https://devguide.python.org/developer-workflow/c-api/index.html>`__,
|
||||
and `user documentation <https://docs.python.org/3/c-api/stable.html>`__
|
||||
will be updated accordingly.
|
||||
|
||||
Reference docs for C API named ``PyUnstable_*`` will automatically show
|
||||
notes with links to the unstable tier documentation.
|
||||
|
||||
|
||||
Adjustments during Beta periods
|
||||
-------------------------------
|
||||
Leading underscore
|
||||
------------------
|
||||
|
||||
New APIs can be added to the unstable tier, and private APIs can be moved
|
||||
to it, up to the first release candidate of a new minor version.
|
||||
Consensus on the ``capi-sig`` or ``python-dev`` is needed in the Beta period.
|
||||
C API named with a leading underscore, as well as API only available with
|
||||
``Py_BUILD_CORE``, will be considered *internal*.
|
||||
This means:
|
||||
|
||||
In the Beta period, no API may be moved to more private tier, e.g.
|
||||
what is public in Beta 1 must stay public until the final release.
|
||||
- It may change or be removed *without notice* in minor
|
||||
releases (3.x.0, including Alpha and Beta releases of 3.x.0).
|
||||
API changes in patch releases or Release Candidates should only be done if
|
||||
absolutely necessary.
|
||||
|
||||
- It should be documented in source comments or Devguide only, not in the
|
||||
public documentation.
|
||||
|
||||
- API introduced before Python 3.12 that is documented or widely used
|
||||
externally should be moved to the Unstable tier as explained above.
|
||||
|
||||
This might happen long after this PEP is accepted.
|
||||
Consequently, for a few years core devs should do some research before
|
||||
changing underscored API, especially if it doesn't need ``Py_BUILD_CORE``.
|
||||
|
||||
Users of the C API are encouraged to search their codebase for ``_Py`` and
|
||||
``_PY`` identifier prefixes, and treat any hits as issues to be eventually
|
||||
fixed -- either by switching to an existing alternative, or by opening
|
||||
a CPython issue to request exposing public API for their use case,
|
||||
and eventually switching to that.
|
||||
|
||||
|
||||
.. _pep689-initial-list:
|
||||
|
||||
Initial unstable API
|
||||
--------------------
|
||||
|
||||
The following API will initially be unstable.
|
||||
The set may be adjusted for 3.11.
|
||||
The following API will be moved to the Unstable tier in the initial
|
||||
implementation as proof of the concept.
|
||||
|
||||
Code object constructors:
|
||||
|
||||
- ``PyCode_New()``
|
||||
- ``PyCode_NewWithPosOnlyArgs()``
|
||||
- ``PyUnstable_Code_New()`` (renamed from ``PyCode_New``)
|
||||
- ``PyUnstable_Code_NewWithPosOnlyArgs()`` (renamed from ``PyCode_NewWithPosOnlyArgs``)
|
||||
|
||||
Frame evaluation API (PEP 523):
|
||||
Frame evaluation API (:pep:`523`):
|
||||
|
||||
- ``_PyFrameEvalFunction``
|
||||
- ``_PyInterpreterState_GetEvalFrameFunc()``
|
||||
- ``_PyInterpreterState_SetEvalFrameFunc()``
|
||||
- ``_PyEval_RequestCodeExtraIndex()``
|
||||
- ``_PyCode_GetExtra()``
|
||||
- ``_PyCode_SetExtra()``
|
||||
- ``struct _PyInterpreterFrame`` (as an incomplete, opaque struct)
|
||||
- ``_PyFrame_GetFrameObject``
|
||||
- ``PyEval_EvalFrameDefault``
|
||||
- ``PyUnstable_FrameEvalFunction`` (renamed from ``_PyFrameEvalFunction``)
|
||||
- ``PyUnstable_InterpreterState_GetEvalFrameFunc()`` (renamed from ``_PyInterpreterState_GetEvalFrameFunc``)
|
||||
- ``PyUnstable_InterpreterState_SetEvalFrameFunc()`` (renamed from ``_PyInterpreterState_SetEvalFrameFunc``)
|
||||
- ``PyUnstable_Eval_RequestCodeExtraIndex()`` (renamed from ``_PyEval_RequestCodeExtraIndex``)
|
||||
- ``PyUnstable_Code_GetExtra()`` (renamed from ``_PyCode_GetExtra``)
|
||||
- ``PyUnstable_Code_SetExtra()`` (renamed from ``_PyCode_SetExtra``)
|
||||
- ``PyUnstable_InterpreterFrame`` (typedef for ``_PyInterpreterFrame``, as an opaque struct)
|
||||
- ``PyUnstable_Frame_GetFrameObject`` (renamed from ``_PyFrame_GetFrameObject``)
|
||||
- ``PyUnstable_EvalFrameDefault``
|
||||
(new function that calls ``_PyEval_EvalFrameDefault``, but takes
|
||||
``PyFrameObject`` rather than ``_PyInterpreterFrame``)
|
||||
|
||||
(Leading underscores will be removed as mentioned above.)
|
||||
|
||||
|
||||
Backwards Compatibility
|
||||
=======================
|
||||
|
||||
The C API backwards compatibility story will be made clearer.
|
||||
The C API backwards compatibility expectations will be made clearer.
|
||||
|
||||
All renamed API will be available under old names for as long as feasible.
|
||||
|
||||
|
||||
How to Teach This
|
||||
=================
|
||||
|
||||
The changes affect advanced C programmers, who should consult the
|
||||
updated reference documentation, devguide and/or What's New document·.
|
||||
updated reference documentation, devguide and/or What's New document.
|
||||
|
||||
|
||||
Reference Implementation
|
||||
|
@ -213,19 +250,43 @@ Reference Implementation
|
|||
https://github.com/python/cpython/issues/91744
|
||||
|
||||
|
||||
Rejected Ideas
|
||||
==============
|
||||
|
||||
It might be good to add a similar tier in the Python (not C) API,
|
||||
e.g. for ``types.CodeType``.
|
||||
However, the opt-in mechanism would need to be different (if any).
|
||||
This is outside the scope of the PEP.
|
||||
|
||||
|
||||
Open Issues
|
||||
===========
|
||||
|
||||
The exact set of exposed API may change.
|
||||
With the ``PyUnstable_`` prefix, is the opt-in macro necessary?
|
||||
|
||||
|
||||
Rejected Ideas
|
||||
==============
|
||||
|
||||
No special prefix
|
||||
-----------------
|
||||
|
||||
In the initial version of this PEP, unstable API didn't have the ``PyUnstable``
|
||||
prefix.
|
||||
Instead, defining ``Py_USING_UNSTABLE_API`` made the API available in a given
|
||||
source file, signifying acknowledgement that the file as a whole will
|
||||
potentially need to be revisited for each Python release.
|
||||
|
||||
However, it was decided that unstable-ness needs to be exposed
|
||||
in the individual names.
|
||||
|
||||
Underscore prefix
|
||||
-----------------
|
||||
|
||||
It would be possible to mark both private and unstable API with
|
||||
leading underscores.
|
||||
However, that would dilute the meaning of ``_Py`` prefix.
|
||||
Reserving the prefix for internal API only makes it trivial to search for.
|
||||
|
||||
|
||||
Python API
|
||||
----------
|
||||
|
||||
It might be good to add a similar tier in the Python (not C) API,
|
||||
e.g. for ``types.CodeType``.
|
||||
However, the mechanism for that would need to be different.
|
||||
This is outside the scope of the PEP.
|
||||
|
||||
|
||||
Copyright
|
||||
|
|
Loading…
Reference in New Issue