cpython/Doc/tools/sphinxext/c_annotations.py

121 lines
3.9 KiB
Python
Raw Normal View History

# -*- 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['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