Introduce support for documenting which C API elements are not part of the stable/limited API.
diff --git a/Doc/c-api/index.rst b/Doc/c-api/index.rst
index 591136e..3bfbaf4 100644
--- a/Doc/c-api/index.rst
+++ b/Doc/c-api/index.rst
@@ -13,6 +13,7 @@
:maxdepth: 2
intro.rst
+ stable.rst
veryhigh.rst
refcounting.rst
exceptions.rst
@@ -22,5 +23,4 @@
init.rst
memory.rst
objimpl.rst
- stable.rst
apiabiversion.rst
diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst
index a7c9ef9..063f856 100644
--- a/Doc/c-api/stable.rst
+++ b/Doc/c-api/stable.rst
@@ -6,36 +6,33 @@
Stable Application Binary Interface
***********************************
-Traditionally, the C API of Python will change with every release.
-Most changes will be source-compatible, typically by only adding API,
-rather than changing existing API or removing API (although some
-interfaces do get removed after being deprecated first).
+Traditionally, the C API of Python will change with every release. Most changes
+will be source-compatible, typically by only adding API, rather than changing
+existing API or removing API (although some interfaces do get removed after
+being deprecated first).
-Unfortunately, the API compatibility does not extend to binary
-compatibility (the ABI). The reason is primarily the evolution of
-struct definitions, where addition of a new field, or changing
-the type of a field, might not break the API, but can break the ABI.
-As a consequence, extension modules need to be recompiled for
-every Python release (although an exception is possible on Unix
-when none of the affected interfaces are used). In addition, on
-Windows, extension modules link with a specific pythonXY.dll and
-need to be recompiled to link with a newer one.
+Unfortunately, the API compatibility does not extend to binary compatibility
+(the ABI). The reason is primarily the evolution of struct definitions, where
+addition of a new field, or changing the type of a field, might not break the
+API, but can break the ABI. As a consequence, extension modules need to be
+recompiled for every Python release (although an exception is possible on Unix
+when none of the affected interfaces are used). In addition, on Windows,
+extension modules link with a specific pythonXY.dll and need to be recompiled to
+link with a newer one.
-Since Python 3.2, a subset of the API has been declared to guarantee
-a stable ABI. Extension modules wishing to use this API need to define
-``Py_LIMITED_API``. A number of interpreter details then become hidden
-from the extension module; in return, a module is built that works
-on any 3.x version (x>=2) without recompilation.
+Since Python 3.2, a subset of the API has been declared to guarantee a stable
+ABI. Extension modules wishing to use this API (called "limited API") need to
+define ``Py_LIMITED_API``. A number of interpreter details then become hidden
+from the extension module; in return, a module is built that works on any 3.x
+version (x>=2) without recompilation.
In some cases, the stable ABI needs to be extended with new functions.
-Extension modules wishing to use these new APIs need to set
-``Py_LIMITED_API`` to the ``PY_VERSION_HEX`` value (see
-:ref:`apiabiversion`) of the minimum Python version they want to
-support (e.g. ``0x03030000`` for Python 3.3). Such modules will work
-on all subsequent Python releases, but fail to load (because of
+Extension modules wishing to use these new APIs need to set ``Py_LIMITED_API``
+to the ``PY_VERSION_HEX`` value (see :ref:`apiabiversion`) of the minimum Python
+version they want to support (e.g. ``0x03030000`` for Python 3.3). Such modules
+will work on all subsequent Python releases, but fail to load (because of
missing symbols) on the older releases.
-As of Python 3.2, the set of functions available to the limited API
-is documented in PEP 384.
-
-.. XXX copy exact list here? Into each functions definition?
+As of Python 3.2, the set of functions available to the limited API is
+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."
diff --git a/Doc/conf.py b/Doc/conf.py
index 6b085e0..5b63cad 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -12,8 +12,8 @@
# General configuration
# ---------------------
-extensions = ['sphinx.ext.refcounting', 'sphinx.ext.coverage',
- 'sphinx.ext.doctest', 'pyspecific']
+extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest',
+ 'pyspecific', 'c_annotations']
templates_path = ['tools/sphinxext']
# General substitutions.
diff --git a/Doc/tools/sphinxext/c_annotations.py b/Doc/tools/sphinxext/c_annotations.py
new file mode 100644
index 0000000..c93fb7c
--- /dev/null
+++ b/Doc/tools/sphinxext/c_annotations.py
@@ -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
diff --git a/Doc/tools/sphinxext/pydoctheme/static/pydoctheme.css b/Doc/tools/sphinxext/pydoctheme/static/pydoctheme.css
index 9942ca6..303529b 100644
--- a/Doc/tools/sphinxext/pydoctheme/static/pydoctheme.css
+++ b/Doc/tools/sphinxext/pydoctheme/static/pydoctheme.css
@@ -168,3 +168,11 @@
div.footer a:hover {
color: #0095C4;
}
+
+.refcount {
+ color: #060;
+}
+
+.notlimited {
+ color: #922;
+}