blob: baa39f3b44646ad5f55fd95766ba04be5fd1a9c5 [file] [log] [blame]
Georg Brandl5c01d992013-10-12 19:54:30 +02001# -*- 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
Georg Brandlbae334c2014-09-30 22:17:41 +020016 :copyright: Copyright 2007-2014 by Georg Brandl.
Georg Brandl5c01d992013-10-12 19:54:30 +020017 :license: Python license.
18"""
19
20from os import path
21from docutils import nodes
22from docutils.parsers.rst import directives
23
24from sphinx import addnodes
25from sphinx.domains.c import CObject
26
27
28class RCEntry:
29 def __init__(self, name):
30 self.name = name
31 self.args = []
32 self.result_type = ''
33 self.result_refs = None
34
35
36class 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
Georg Brandla1403482013-10-12 22:55:34 +020076 if par['stableabi']:
77 node.insert(0, nodes.emphasis(' Part of the stable ABI.',
78 ' Part of the stable ABI.',
79 classes=['stableabi']))
Georg Brandl5c01d992013-10-12 19:54:30 +020080 if par['objtype'] != 'function':
81 continue
82 if not par[0].has_key('names') or not par[0]['names']:
83 continue
Benjamin Petersond065c482014-04-17 18:29:01 -040084 name = par[0]['names'][0]
85 if name.startswith("c."):
86 name = name[2:]
87 entry = self.get(name)
Georg Brandl5c01d992013-10-12 19:54:30 +020088 if not entry:
89 continue
90 elif entry.result_type not in ("PyObject*", "PyVarObject*"):
91 continue
92 if entry.result_refs is None:
93 rc = 'Return value: Always NULL.'
94 elif entry.result_refs:
95 rc = 'Return value: New reference.'
96 else:
97 rc = 'Return value: Borrowed reference.'
98 node.insert(0, nodes.emphasis(rc, rc, classes=['refcount']))
99
100
101def init_annotations(app):
102 refcounts = Annotations.fromfile(
103 path.join(app.srcdir, app.config.refcount_file))
104 app.connect('doctree-read', refcounts.add_annotations)
105
106
107def setup(app):
108 app.add_config_value('refcount_file', '', True)
109 app.connect('builder-inited', init_annotations)
110
111 # monkey-patch C object...
112 CObject.option_spec = {
113 'noindex': directives.flag,
Georg Brandla1403482013-10-12 22:55:34 +0200114 'stableabi': directives.flag,
Georg Brandl5c01d992013-10-12 19:54:30 +0200115 }
116 old_handle_signature = CObject.handle_signature
117 def new_handle_signature(self, sig, signode):
Georg Brandla1403482013-10-12 22:55:34 +0200118 signode.parent['stableabi'] = 'stableabi' in self.options
Georg Brandl5c01d992013-10-12 19:54:30 +0200119 return old_handle_signature(self, sig, signode)
120 CObject.handle_signature = new_handle_signature
Georg Brandlbae334c2014-09-30 22:17:41 +0200121 return {'version': '1.0', 'parallel_read_safe': True}