Skip to content

Commit 5c01d99

Browse files
committed
Introduce support for documenting which C API elements are not part of the stable/limited API.
1 parent 5db7c54 commit 5c01d99

File tree

5 files changed

+154
-32
lines changed

5 files changed

+154
-32
lines changed

Doc/c-api/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ document the API functions in detail.
1313
:maxdepth: 2
1414

1515
intro.rst
16+
stable.rst
1617
veryhigh.rst
1718
refcounting.rst
1819
exceptions.rst
@@ -22,5 +23,4 @@ document the API functions in detail.
2223
init.rst
2324
memory.rst
2425
objimpl.rst
25-
stable.rst
2626
apiabiversion.rst

Doc/c-api/stable.rst

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,33 @@
66
Stable Application Binary Interface
77
***********************************
88

9-
Traditionally, the C API of Python will change with every release.
10-
Most changes will be source-compatible, typically by only adding API,
11-
rather than changing existing API or removing API (although some
12-
interfaces do get removed after being deprecated first).
13-
14-
Unfortunately, the API compatibility does not extend to binary
15-
compatibility (the ABI). The reason is primarily the evolution of
16-
struct definitions, where addition of a new field, or changing
17-
the type of a field, might not break the API, but can break the ABI.
18-
As a consequence, extension modules need to be recompiled for
19-
every Python release (although an exception is possible on Unix
20-
when none of the affected interfaces are used). In addition, on
21-
Windows, extension modules link with a specific pythonXY.dll and
22-
need to be recompiled to link with a newer one.
23-
24-
Since Python 3.2, a subset of the API has been declared to guarantee
25-
a stable ABI. Extension modules wishing to use this API need to define
26-
``Py_LIMITED_API``. A number of interpreter details then become hidden
27-
from the extension module; in return, a module is built that works
28-
on any 3.x version (x>=2) without recompilation.
9+
Traditionally, the C API of Python will change with every release. Most changes
10+
will be source-compatible, typically by only adding API, rather than changing
11+
existing API or removing API (although some interfaces do get removed after
12+
being deprecated first).
13+
14+
Unfortunately, the API compatibility does not extend to binary compatibility
15+
(the ABI). The reason is primarily the evolution of struct definitions, where
16+
addition of a new field, or changing the type of a field, might not break the
17+
API, but can break the ABI. As a consequence, extension modules need to be
18+
recompiled for every Python release (although an exception is possible on Unix
19+
when none of the affected interfaces are used). In addition, on Windows,
20+
extension modules link with a specific pythonXY.dll and need to be recompiled to
21+
link with a newer one.
22+
23+
Since Python 3.2, a subset of the API has been declared to guarantee a stable
24+
ABI. Extension modules wishing to use this API (called "limited API") need to
25+
define ``Py_LIMITED_API``. A number of interpreter details then become hidden
26+
from the extension module; in return, a module is built that works on any 3.x
27+
version (x>=2) without recompilation.
2928

3029
In some cases, the stable ABI needs to be extended with new functions.
31-
Extension modules wishing to use these new APIs need to set
32-
``Py_LIMITED_API`` to the ``PY_VERSION_HEX`` value (see
33-
:ref:`apiabiversion`) of the minimum Python version they want to
34-
support (e.g. ``0x03030000`` for Python 3.3). Such modules will work
35-
on all subsequent Python releases, but fail to load (because of
30+
Extension modules wishing to use these new APIs need to set ``Py_LIMITED_API``
31+
to the ``PY_VERSION_HEX`` value (see :ref:`apiabiversion`) of the minimum Python
32+
version they want to support (e.g. ``0x03030000`` for Python 3.3). Such modules
33+
will work on all subsequent Python releases, but fail to load (because of
3634
missing symbols) on the older releases.
3735

38-
As of Python 3.2, the set of functions available to the limited API
39-
is documented in PEP 384.
40-
41-
.. XXX copy exact list here? Into each functions definition?
36+
As of Python 3.2, the set of functions available to the limited API is
37+
documented in PEP 384. In the C API documentation, API elements that are not
38+
part of the limited API are marked as "Not part of the limited API."

Doc/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
# General configuration
1313
# ---------------------
1414

15-
extensions = ['sphinx.ext.refcounting', 'sphinx.ext.coverage',
16-
'sphinx.ext.doctest', 'pyspecific']
15+
extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest',
16+
'pyspecific', 'c_annotations']
1717
templates_path = ['tools/sphinxext']
1818

1919
# General substitutions.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
c_annotations.py
4+
~~~~~~~~~~~~~~~~
5+
6+
Supports annotations for C API elements:
7+
8+
* reference count annotations for C API functions. Based on
9+
refcount.py and anno-api.py in the old Python documentation tools.
10+
11+
* stable API annotations
12+
13+
Usage: Set the `refcount_file` config value to the path to the reference
14+
count data file.
15+
16+
:copyright: Copyright 2007-2013 by Georg Brandl.
17+
:license: Python license.
18+
"""
19+
20+
from os import path
21+
from docutils import nodes
22+
from docutils.parsers.rst import directives
23+
24+
from sphinx import addnodes
25+
from sphinx.domains.c import CObject
26+
27+
28+
class RCEntry:
29+
def __init__(self, name):
30+
self.name = name
31+
self.args = []
32+
self.result_type = ''
33+
self.result_refs = None
34+
35+
36+
class Annotations(dict):
37+
@classmethod
38+
def fromfile(cls, filename):
39+
d = cls()
40+
fp = open(filename, 'r')
41+
try:
42+
for line in fp:
43+
line = line.strip()
44+
if line[:1] in ("", "#"):
45+
# blank lines and comments
46+
continue
47+
parts = line.split(":", 4)
48+
if len(parts) != 5:
49+
raise ValueError("Wrong field count in %r" % line)
50+
function, type, arg, refcount, comment = parts
51+
# Get the entry, creating it if needed:
52+
try:
53+
entry = d[function]
54+
except KeyError:
55+
entry = d[function] = RCEntry(function)
56+
if not refcount or refcount == "null":
57+
refcount = None
58+
else:
59+
refcount = int(refcount)
60+
# Update the entry with the new parameter or the result
61+
# information.
62+
if arg:
63+
entry.args.append((arg, type, refcount))
64+
else:
65+
entry.result_type = type
66+
entry.result_refs = refcount
67+
finally:
68+
fp.close()
69+
return d
70+
71+
def add_annotations(self, app, doctree):
72+
for node in doctree.traverse(addnodes.desc_content):
73+
par = node.parent
74+
if par['domain'] != 'c':
75+
continue
76+
if par['notlimited']:
77+
node.insert(0, nodes.emphasis(' Not part of the stable API.',
78+
' Not part of the stable API.',
79+
classes=['notlimited']))
80+
if par['objtype'] != 'function':
81+
continue
82+
if not par[0].has_key('names') or not par[0]['names']:
83+
continue
84+
entry = self.get(par[0]['names'][0])
85+
if not entry:
86+
continue
87+
elif entry.result_type not in ("PyObject*", "PyVarObject*"):
88+
continue
89+
if entry.result_refs is None:
90+
rc = 'Return value: Always NULL.'
91+
elif entry.result_refs:
92+
rc = 'Return value: New reference.'
93+
else:
94+
rc = 'Return value: Borrowed reference.'
95+
node.insert(0, nodes.emphasis(rc, rc, classes=['refcount']))
96+
97+
98+
def init_annotations(app):
99+
refcounts = Annotations.fromfile(
100+
path.join(app.srcdir, app.config.refcount_file))
101+
app.connect('doctree-read', refcounts.add_annotations)
102+
103+
104+
def setup(app):
105+
app.add_config_value('refcount_file', '', True)
106+
app.connect('builder-inited', init_annotations)
107+
108+
# monkey-patch C object...
109+
CObject.option_spec = {
110+
'noindex': directives.flag,
111+
'notlimited': directives.flag,
112+
}
113+
old_handle_signature = CObject.handle_signature
114+
def new_handle_signature(self, sig, signode):
115+
signode.parent['notlimited'] = 'notlimited' in self.options
116+
return old_handle_signature(self, sig, signode)
117+
CObject.handle_signature = new_handle_signature

Doc/tools/sphinxext/pydoctheme/static/pydoctheme.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,11 @@ div.footer {
168168
div.footer a:hover {
169169
color: #0095C4;
170170
}
171+
172+
.refcount {
173+
color: #060;
174+
}
175+
176+
.notlimited {
177+
color: #922;
178+
}

0 commit comments

Comments
 (0)