mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-91271: Document which parts of structs are in limited API/stable ABI (GH-32196) (GH-95711)
Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
This commit is contained in:
parent
57446f9e33
commit
b66b6e1cc0
5 changed files with 982 additions and 871 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,7 @@
|
||||||
'type': 'type',
|
'type': 'type',
|
||||||
'macro': 'macro',
|
'macro': 'macro',
|
||||||
'type': 'type',
|
'type': 'type',
|
||||||
|
'member': 'member',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -100,6 +101,12 @@ def add_annotations(self, app, doctree):
|
||||||
# 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 @@ def add_annotations(self, app, doctree):
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
The documentation now lists which members of C structs are part of the
|
||||||
|
:ref:`Limited API/Stable ABI <stable>`.
|
||||||
|
|
@ -10,44 +10,111 @@
|
||||||
# and PC/pythonXYstub.def
|
# and PC/pythonXYstub.def
|
||||||
|
|
||||||
|
|
||||||
|
# The current format is a simple line-based one with significant indentation.
|
||||||
|
# Anything after a hash is a comment.
|
||||||
|
|
||||||
|
# There are these kinds of top-level "items":
|
||||||
|
# - struct: A C struct. Currently this file does not distinguish between:
|
||||||
|
# - opaque structs, which the Limited API only handles via pointers
|
||||||
|
# (so these can change at any time)
|
||||||
|
# - structs where only certain members are part of the stable ABI (e.g.
|
||||||
|
# PyObject)
|
||||||
|
# - structs which must not be changed at all (e.g. PyType_Slot, which is
|
||||||
|
# fully defined and used in arrays)
|
||||||
|
# - function: A function that must be kept available (and exported, i.e. not
|
||||||
|
# converted to a macro).
|
||||||
|
# - const: A simple value, defined with `#define`.
|
||||||
|
# - macro: A preprocessor macro more complex than a simple `const` value.
|
||||||
|
# - data: An exported object, which must continue to be available but its exact
|
||||||
|
# value may change.
|
||||||
|
# - typedef: A C typedef which is used in other definitions in the limited API.
|
||||||
|
# Its size/layout/signature must not change.
|
||||||
|
|
||||||
|
# Each top-level item can have details defined below it:
|
||||||
|
# - added: The version in which the item was added to the stable ABI.
|
||||||
|
# - ifdef: A feature macro: the item is only available if this macro is defined
|
||||||
|
# - abi_only: If present, the item is not part of the Limited API, but it *is*
|
||||||
|
# part of the stable ABI. The item will not show up in user-facing docs.
|
||||||
|
# Typically used for:
|
||||||
|
# - private functions called by public macros, e.g. _Py_BuildValue_SizeT
|
||||||
|
# - items that were part of the limited API in the past, and must remain part
|
||||||
|
# of the stable ABI.
|
||||||
|
# - a combination of the above (functions that were called by macros that
|
||||||
|
# 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
|
||||||
|
# be considered with that in mind. See the devguide for exact rules:
|
||||||
|
# https://devguide.python.org/c-api/#limited-api
|
||||||
|
|
||||||
|
# User-facing docs are at:
|
||||||
|
# https://docs.python.org/3/c-api/stable.html#stable
|
||||||
|
|
||||||
|
|
||||||
# Mentioned in PEP 384:
|
# Mentioned in PEP 384:
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -259,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
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,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',
|
||||||
|
|
@ -173,6 +175,15 @@ def raise_error(msg):
|
||||||
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}")
|
||||||
levels.append((entry, level))
|
levels.append((entry, level))
|
||||||
|
|
@ -246,7 +257,9 @@ def sort_key(item):
|
||||||
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})
|
||||||
|
|
||||||
def generate_or_check(manifest, args, path, func):
|
def generate_or_check(manifest, args, path, func):
|
||||||
"""Generate/check a file with a single generator
|
"""Generate/check a file with a single generator
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue