bpo-47115: Document which parts of structs are in limited API/stable ABI (GH-32196)

Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
This commit is contained in:
Petr Viktorin 2022-04-06 16:50:45 +02:00 committed by GitHub
parent 14a9b4895b
commit d79f118d04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 969 additions and 893 deletions

View file

@ -7,10 +7,12 @@ Frame Objects
The C structure of the objects used to describe frame objects. The C structure of the objects used to describe frame objects.
The structure is not part of the C API. There are no public members in this structure.
.. versionchanged:: 3.11 .. versionchanged:: 3.11
The structure moved to the internal C API headers. The members of this structure were removed from the public C API.
Refer to the :ref:`What's New entry <pyframeobject-3.11-hiding>`
for details.
The :c:func:`PyEval_GetFrame` and :c:func:`PyThreadState_GetFrame` functions The :c:func:`PyEval_GetFrame` and :c:func:`PyThreadState_GetFrame` functions
can be used to get a frame object. can be used to get a frame object.

1766
Doc/data/stable_abi.dat generated

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,7 @@ REST_ROLE_MAP = {
'type': 'type', 'type': 'type',
'macro': 'macro', 'macro': 'macro',
'type': 'type', 'type': 'type',
'member': 'member',
} }
@ -100,6 +101,12 @@ class Annotations:
# Stable ABI annotation. These have two forms: # Stable ABI annotation. These have two forms:
# Part of the [Stable ABI](link). # Part of the [Stable ABI](link).
# Part of the [Stable ABI](link) since version X.Y. # Part of the [Stable ABI](link) since version X.Y.
# For structs, there's some more info in the message:
# Part of the [Limited API](link) (as an opaque struct).
# Part of the [Stable ABI](link) (including all members).
# Part of the [Limited API](link) (Only some members are part
# of the stable ABI.).
# ... all of which can have "since version X.Y" appended.
record = self.stable_abi_data.get(name) record = self.stable_abi_data.get(name)
if record: if record:
if record['role'] != objtype: if record['role'] != objtype:
@ -113,15 +120,27 @@ class Annotations:
ref_node = addnodes.pending_xref( ref_node = addnodes.pending_xref(
'Stable ABI', refdomain="std", reftarget='stable', 'Stable ABI', refdomain="std", reftarget='stable',
reftype='ref', refexplicit="False") reftype='ref', refexplicit="False")
struct_abi_kind = record['struct_abi_kind']
if struct_abi_kind in {'opaque', 'members'}:
ref_node += nodes.Text('Limited API')
else:
ref_node += nodes.Text('Stable ABI') ref_node += nodes.Text('Stable ABI')
emph_node += ref_node emph_node += ref_node
if struct_abi_kind == 'opaque':
emph_node += nodes.Text(' (as an opaque struct)')
elif struct_abi_kind == 'full-abi':
emph_node += nodes.Text(' (including all members)')
if record['ifdef_note']: if record['ifdef_note']:
emph_node += nodes.Text(' ' + record['ifdef_note']) emph_node += nodes.Text(' ' + record['ifdef_note'])
if stable_added == '3.2': if stable_added == '3.2':
# Stable ABI was introduced in 3.2. # Stable ABI was introduced in 3.2.
emph_node += nodes.Text('.') pass
else: else:
emph_node += nodes.Text(f' since version {stable_added}.') emph_node += nodes.Text(f' since version {stable_added}')
emph_node += nodes.Text('.')
if struct_abi_kind == 'members':
emph_node += nodes.Text(
' (Only some members are part of the stable ABI.)')
node.insert(0, emph_node) node.insert(0, emph_node)
# Return value annotation # Return value annotation

View file

@ -1214,8 +1214,10 @@ Porting to Python 3.11
which are not available in the limited C API. which are not available in the limited C API.
(Contributed by Victor Stinner in :issue:`46007`.) (Contributed by Victor Stinner in :issue:`46007`.)
* The :c:type:`PyFrameObject` structure member has been moved to the internal C .. _pyframeobject-3.11-hiding:
API headers.
* The :c:type:`PyFrameObject` structure members have been removed from the
public C API.
While the documentation notes that the :c:type:`PyFrameObject` fields are While the documentation notes that the :c:type:`PyFrameObject` fields are
subject to change at any time, they have been stable for a long time and were subject to change at any time, they have been stable for a long time and were

View file

@ -0,0 +1,2 @@
The documentation now lists which members of C structs are part of the
:ref:`Limited API/Stable ABI <stable>`.

View file

@ -42,6 +42,15 @@
# - a combination of the above (functions that were called by macros that # - a combination of the above (functions that were called by macros that
# were public in the past) # were public in the past)
# For structs, one of the following must be set:
# - opaque: The struct name is available in the Limited API, but its members
# are not. Users must manipulate it via pointers.
# - members: Space-separated list of members which are part of the
# Limited API and Stable ABI.
# Members that aren't listed are not accessible to applications.
# - full-abi: The entire struct -- all its members and its size -- is part of
# the Stable ABI, and must not change.
# Removing items from this file is generally not allowed, and additions should # Removing items from this file is generally not allowed, and additions should
# be considered with that in mind. See the devguide for exact rules: # be considered with that in mind. See the devguide for exact rules:
# https://devguide.python.org/c-api/#limited-api # https://devguide.python.org/c-api/#limited-api
@ -54,40 +63,58 @@
struct PyObject struct PyObject
added 3.2 added 3.2
members ob_refcnt ob_type
struct PyVarObject struct PyVarObject
added 3.2 added 3.2
members ob_base ob_size
struct PyMethodDef struct PyMethodDef
added 3.2 added 3.2
full-abi
struct PyMemberDef struct PyMemberDef
added 3.2 added 3.2
full-abi
struct PyGetSetDef struct PyGetSetDef
added 3.2 added 3.2
full-abi
struct PyModuleDef_Base struct PyModuleDef_Base
added 3.2 added 3.2
full-abi
struct PyModuleDef struct PyModuleDef
added 3.2 added 3.2
full-abi
struct PyStructSequence_Field struct PyStructSequence_Field
added 3.2 added 3.2
full-abi
struct PyStructSequence_Desc struct PyStructSequence_Desc
added 3.2 added 3.2
full-abi
struct PyType_Slot struct PyType_Slot
added 3.2 added 3.2
full-abi
struct PyType_Spec struct PyType_Spec
added 3.2 added 3.2
full-abi
struct PyThreadState struct PyThreadState
added 3.2 added 3.2
opaque
struct PyInterpreterState struct PyInterpreterState
added 3.2 added 3.2
opaque
struct PyFrameObject struct PyFrameObject
added 3.2 added 3.2
opaque
struct symtable struct symtable
added 3.2 added 3.2
opaque
struct PyWeakReference struct PyWeakReference
added 3.2 added 3.2
opaque
struct PyLongObject struct PyLongObject
added 3.2 added 3.2
opaque
struct PyTypeObject struct PyTypeObject
added 3.2 added 3.2
opaque
function PyType_FromSpec function PyType_FromSpec
added 3.2 added 3.2
@ -299,11 +326,11 @@ typedef newfunc
added 3.2 added 3.2
typedef allocfunc typedef allocfunc
added 3.2 added 3.2
struct PyCFunction typedef PyCFunction
added 3.2 added 3.2
struct PyCFunctionWithKeywords typedef PyCFunctionWithKeywords
added 3.2 added 3.2
struct PyCapsule_Destructor typedef PyCapsule_Destructor
added 3.2 added 3.2
typedef getter typedef getter
added 3.2 added 3.2
@ -2194,6 +2221,7 @@ data PyStructSequence_UnnamedField
# Add stable Py_buffer API in Python 3.11 (https://bugs.python.org/issue45459) # Add stable Py_buffer API in Python 3.11 (https://bugs.python.org/issue45459)
struct Py_buffer struct Py_buffer
added 3.11 added 3.11
full-abi
function PyObject_CheckBuffer function PyObject_CheckBuffer
added 3.11 added 3.11
function PyObject_GetBuffer function PyObject_GetBuffer

View file

@ -118,6 +118,8 @@ class ABIItem:
contents: list = dataclasses.field(default_factory=list) contents: list = dataclasses.field(default_factory=list)
abi_only: bool = False abi_only: bool = False
ifdef: str = None ifdef: str = None
struct_abi_kind: str = None
members: list = None
KINDS = frozenset({ KINDS = frozenset({
'struct', 'function', 'macro', 'data', 'const', 'typedef', 'struct', 'function', 'macro', 'data', 'const', 'typedef',
@ -172,6 +174,15 @@ def parse_manifest(file):
if parent.kind not in {'function', 'data'}: if parent.kind not in {'function', 'data'}:
raise_error(f'{kind} cannot go in {parent.kind}') raise_error(f'{kind} cannot go in {parent.kind}')
parent.abi_only = True parent.abi_only = True
elif kind in {'members', 'full-abi', 'opaque'}:
if parent.kind not in {'struct'}:
raise_error(f'{kind} cannot go in {parent.kind}')
if prev := getattr(parent, 'struct_abi_kind', None):
raise_error(
f'{parent.name} already has {prev}, cannot add {kind}')
parent.struct_abi_kind = kind
if kind == 'members':
parent.members = content.split()
else: else:
raise_error(f"unknown kind {kind!r}") raise_error(f"unknown kind {kind!r}")
# When adding more, update the comment in stable_abi.txt. # When adding more, update the comment in stable_abi.txt.
@ -246,7 +257,9 @@ REST_ROLES = {
def gen_doc_annotations(manifest, args, outfile): def gen_doc_annotations(manifest, args, outfile):
"""Generate/check the stable ABI list for documentation annotations""" """Generate/check the stable ABI list for documentation annotations"""
writer = csv.DictWriter( writer = csv.DictWriter(
outfile, ['role', 'name', 'added', 'ifdef_note'], lineterminator='\n') outfile,
['role', 'name', 'added', 'ifdef_note', 'struct_abi_kind'],
lineterminator='\n')
writer.writeheader() writer.writeheader()
for item in manifest.select(REST_ROLES.keys(), include_abi_only=False): for item in manifest.select(REST_ROLES.keys(), include_abi_only=False):
if item.ifdef: if item.ifdef:
@ -257,7 +270,13 @@ def gen_doc_annotations(manifest, args, outfile):
'role': REST_ROLES[item.kind], 'role': REST_ROLES[item.kind],
'name': item.name, 'name': item.name,
'added': item.added, 'added': item.added,
'ifdef_note': ifdef_note}) 'ifdef_note': ifdef_note,
'struct_abi_kind': item.struct_abi_kind})
for member_name in item.members or ():
writer.writerow({
'role': 'member',
'name': f'{item.name}.{member_name}',
'added': item.added})
@generator("ctypes_test", 'Lib/test/test_stable_abi_ctypes.py') @generator("ctypes_test", 'Lib/test/test_stable_abi_ctypes.py')
def gen_ctypes_test(manifest, args, outfile): def gen_ctypes_test(manifest, args, outfile):