1# -*- 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 16 :copyright: Copyright 2007-2014 by Georg Brandl. 17 :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 76 if par['stableabi']: 77 node.insert(0, nodes.emphasis(' Part of the stable ABI.', 78 ' Part of the stable ABI.', 79 classes=['stableabi'])) 80 if par['objtype'] != 'function': 81 continue 82 if not par[0].has_key('names') or not par[0]['names']: 83 continue 84 name = par[0]['names'][0] 85 if name.startswith("c."): 86 name = name[2:] 87 entry = self.get(name) 88 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, 114 'stableabi': directives.flag, 115 } 116 old_handle_signature = CObject.handle_signature 117 def new_handle_signature(self, sig, signode): 118 signode.parent['stableabi'] = 'stableabi' in self.options 119 return old_handle_signature(self, sig, signode) 120 CObject.handle_signature = new_handle_signature 121 return {'version': '1.0', 'parallel_read_safe': True} 122