Introduce support for documenting which C API elements are not part of the stable/limited API.
This commit is contained in:
parent
5db7c54f96
commit
5c01d99c12
@ -13,6 +13,7 @@ document the API functions in detail.
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
intro.rst
|
intro.rst
|
||||||
|
stable.rst
|
||||||
veryhigh.rst
|
veryhigh.rst
|
||||||
refcounting.rst
|
refcounting.rst
|
||||||
exceptions.rst
|
exceptions.rst
|
||||||
@ -22,5 +23,4 @@ document the API functions in detail.
|
|||||||
init.rst
|
init.rst
|
||||||
memory.rst
|
memory.rst
|
||||||
objimpl.rst
|
objimpl.rst
|
||||||
stable.rst
|
|
||||||
apiabiversion.rst
|
apiabiversion.rst
|
||||||
|
@ -6,36 +6,33 @@
|
|||||||
Stable Application Binary Interface
|
Stable Application Binary Interface
|
||||||
***********************************
|
***********************************
|
||||||
|
|
||||||
Traditionally, the C API of Python will change with every release.
|
Traditionally, the C API of Python will change with every release. Most changes
|
||||||
Most changes will be source-compatible, typically by only adding API,
|
will be source-compatible, typically by only adding API, rather than changing
|
||||||
rather than changing existing API or removing API (although some
|
existing API or removing API (although some interfaces do get removed after
|
||||||
interfaces do get removed after being deprecated first).
|
being deprecated first).
|
||||||
|
|
||||||
Unfortunately, the API compatibility does not extend to binary
|
Unfortunately, the API compatibility does not extend to binary compatibility
|
||||||
compatibility (the ABI). The reason is primarily the evolution of
|
(the ABI). The reason is primarily the evolution of struct definitions, where
|
||||||
struct definitions, where addition of a new field, or changing
|
addition of a new field, or changing the type of a field, might not break the
|
||||||
the type of a field, might not break the API, but can break the ABI.
|
API, but can break the ABI. As a consequence, extension modules need to be
|
||||||
As a consequence, extension modules need to be recompiled for
|
recompiled for every Python release (although an exception is possible on Unix
|
||||||
every Python release (although an exception is possible on Unix
|
when none of the affected interfaces are used). In addition, on Windows,
|
||||||
when none of the affected interfaces are used). In addition, on
|
extension modules link with a specific pythonXY.dll and need to be recompiled to
|
||||||
Windows, extension modules link with a specific pythonXY.dll and
|
link with a newer one.
|
||||||
need to be recompiled to link with a newer one.
|
|
||||||
|
|
||||||
Since Python 3.2, a subset of the API has been declared to guarantee
|
Since Python 3.2, a subset of the API has been declared to guarantee a stable
|
||||||
a stable ABI. Extension modules wishing to use this API need to define
|
ABI. Extension modules wishing to use this API (called "limited API") need to
|
||||||
``Py_LIMITED_API``. A number of interpreter details then become hidden
|
define ``Py_LIMITED_API``. A number of interpreter details then become hidden
|
||||||
from the extension module; in return, a module is built that works
|
from the extension module; in return, a module is built that works on any 3.x
|
||||||
on any 3.x version (x>=2) without recompilation.
|
version (x>=2) without recompilation.
|
||||||
|
|
||||||
In some cases, the stable ABI needs to be extended with new functions.
|
In some cases, the stable ABI needs to be extended with new functions.
|
||||||
Extension modules wishing to use these new APIs need to set
|
Extension modules wishing to use these new APIs need to set ``Py_LIMITED_API``
|
||||||
``Py_LIMITED_API`` to the ``PY_VERSION_HEX`` value (see
|
to the ``PY_VERSION_HEX`` value (see :ref:`apiabiversion`) of the minimum Python
|
||||||
:ref:`apiabiversion`) of the minimum Python version they want to
|
version they want to support (e.g. ``0x03030000`` for Python 3.3). Such modules
|
||||||
support (e.g. ``0x03030000`` for Python 3.3). Such modules will work
|
will work on all subsequent Python releases, but fail to load (because of
|
||||||
on all subsequent Python releases, but fail to load (because of
|
|
||||||
missing symbols) on the older releases.
|
missing symbols) on the older releases.
|
||||||
|
|
||||||
As of Python 3.2, the set of functions available to the limited API
|
As of Python 3.2, the set of functions available to the limited API is
|
||||||
is documented in PEP 384.
|
documented in PEP 384. In the C API documentation, API elements that are not
|
||||||
|
part of the limited API are marked as "Not part of the limited API."
|
||||||
.. XXX copy exact list here? Into each functions definition?
|
|
||||||
|
@ -12,8 +12,8 @@ sys.path.append(os.path.abspath('tools/sphinxext'))
|
|||||||
# General configuration
|
# General configuration
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
extensions = ['sphinx.ext.refcounting', 'sphinx.ext.coverage',
|
extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest',
|
||||||
'sphinx.ext.doctest', 'pyspecific']
|
'pyspecific', 'c_annotations']
|
||||||
templates_path = ['tools/sphinxext']
|
templates_path = ['tools/sphinxext']
|
||||||
|
|
||||||
# General substitutions.
|
# General substitutions.
|
||||||
|
117
Doc/tools/sphinxext/c_annotations.py
Normal file
117
Doc/tools/sphinxext/c_annotations.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
c_annotations.py
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Supports annotations for C API elements:
|
||||||
|
|
||||||
|
* reference count annotations for C API functions. Based on
|
||||||
|
refcount.py and anno-api.py in the old Python documentation tools.
|
||||||
|
|
||||||
|
* stable API annotations
|
||||||
|
|
||||||
|
Usage: Set the `refcount_file` config value to the path to the reference
|
||||||
|
count data file.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2013 by Georg Brandl.
|
||||||
|
:license: Python license.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import directives
|
||||||
|
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.domains.c import CObject
|
||||||
|
|
||||||
|
|
||||||
|
class RCEntry:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.args = []
|
||||||
|
self.result_type = ''
|
||||||
|
self.result_refs = None
|
||||||
|
|
||||||
|
|
||||||
|
class Annotations(dict):
|
||||||
|
@classmethod
|
||||||
|
def fromfile(cls, filename):
|
||||||
|
d = cls()
|
||||||
|
fp = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
for line in fp:
|
||||||
|
line = line.strip()
|
||||||
|
if line[:1] in ("", "#"):
|
||||||
|
# blank lines and comments
|
||||||
|
continue
|
||||||
|
parts = line.split(":", 4)
|
||||||
|
if len(parts) != 5:
|
||||||
|
raise ValueError("Wrong field count in %r" % line)
|
||||||
|
function, type, arg, refcount, comment = parts
|
||||||
|
# Get the entry, creating it if needed:
|
||||||
|
try:
|
||||||
|
entry = d[function]
|
||||||
|
except KeyError:
|
||||||
|
entry = d[function] = RCEntry(function)
|
||||||
|
if not refcount or refcount == "null":
|
||||||
|
refcount = None
|
||||||
|
else:
|
||||||
|
refcount = int(refcount)
|
||||||
|
# Update the entry with the new parameter or the result
|
||||||
|
# information.
|
||||||
|
if arg:
|
||||||
|
entry.args.append((arg, type, refcount))
|
||||||
|
else:
|
||||||
|
entry.result_type = type
|
||||||
|
entry.result_refs = refcount
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return d
|
||||||
|
|
||||||
|
def add_annotations(self, app, doctree):
|
||||||
|
for node in doctree.traverse(addnodes.desc_content):
|
||||||
|
par = node.parent
|
||||||
|
if par['domain'] != 'c':
|
||||||
|
continue
|
||||||
|
if par['notlimited']:
|
||||||
|
node.insert(0, nodes.emphasis(' Not part of the stable API.',
|
||||||
|
' Not part of the stable API.',
|
||||||
|
classes=['notlimited']))
|
||||||
|
if par['objtype'] != 'function':
|
||||||
|
continue
|
||||||
|
if not par[0].has_key('names') or not par[0]['names']:
|
||||||
|
continue
|
||||||
|
entry = self.get(par[0]['names'][0])
|
||||||
|
if not entry:
|
||||||
|
continue
|
||||||
|
elif entry.result_type not in ("PyObject*", "PyVarObject*"):
|
||||||
|
continue
|
||||||
|
if entry.result_refs is None:
|
||||||
|
rc = 'Return value: Always NULL.'
|
||||||
|
elif entry.result_refs:
|
||||||
|
rc = 'Return value: New reference.'
|
||||||
|
else:
|
||||||
|
rc = 'Return value: Borrowed reference.'
|
||||||
|
node.insert(0, nodes.emphasis(rc, rc, classes=['refcount']))
|
||||||
|
|
||||||
|
|
||||||
|
def init_annotations(app):
|
||||||
|
refcounts = Annotations.fromfile(
|
||||||
|
path.join(app.srcdir, app.config.refcount_file))
|
||||||
|
app.connect('doctree-read', refcounts.add_annotations)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_config_value('refcount_file', '', True)
|
||||||
|
app.connect('builder-inited', init_annotations)
|
||||||
|
|
||||||
|
# monkey-patch C object...
|
||||||
|
CObject.option_spec = {
|
||||||
|
'noindex': directives.flag,
|
||||||
|
'notlimited': directives.flag,
|
||||||
|
}
|
||||||
|
old_handle_signature = CObject.handle_signature
|
||||||
|
def new_handle_signature(self, sig, signode):
|
||||||
|
signode.parent['notlimited'] = 'notlimited' in self.options
|
||||||
|
return old_handle_signature(self, sig, signode)
|
||||||
|
CObject.handle_signature = new_handle_signature
|
@ -168,3 +168,11 @@ div.footer {
|
|||||||
div.footer a:hover {
|
div.footer a:hover {
|
||||||
color: #0095C4;
|
color: #0095C4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.refcount {
|
||||||
|
color: #060;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notlimited {
|
||||||
|
color: #922;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user