summaryrefslogtreecommitdiffstats
path: root/Doc/tools/extensions/c_annotations.py
diff options
context:
space:
mode:
Diffstat (limited to 'Doc/tools/extensions/c_annotations.py')
-rw-r--r--Doc/tools/extensions/c_annotations.py121
1 files changed, 121 insertions, 0 deletions
diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py
new file mode 100644
index 0000000..baa39f3
--- /dev/null
+++ b/Doc/tools/extensions/c_annotations.py
@@ -0,0 +1,121 @@
+# -*- 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-2014 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['stableabi']:
+ node.insert(0, nodes.emphasis(' Part of the stable ABI.',
+ ' Part of the stable ABI.',
+ classes=['stableabi']))
+ if par['objtype'] != 'function':
+ continue
+ if not par[0].has_key('names') or not par[0]['names']:
+ continue
+ name = par[0]['names'][0]
+ if name.startswith("c."):
+ name = name[2:]
+ entry = self.get(name)
+ 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,
+ 'stableabi': directives.flag,
+ }
+ old_handle_signature = CObject.handle_signature
+ def new_handle_signature(self, sig, signode):
+ signode.parent['stableabi'] = 'stableabi' in self.options
+ return old_handle_signature(self, sig, signode)
+ CObject.handle_signature = new_handle_signature
+ return {'version': '1.0', 'parallel_read_safe': True}