Home | History | Annotate | Download | only in extensions
      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 
     20 from os import path
     21 from docutils import nodes
     22 from docutils.parsers.rst import directives
     23 
     24 from sphinx import addnodes
     25 from sphinx.domains.c import CObject
     26 
     27 
     28 class RCEntry:
     29     def __init__(self, name):
     30         self.name = name
     31         self.args = []
     32         self.result_type = ''
     33         self.result_refs = None
     34 
     35 
     36 class 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 not entry.result_type.endswith("Object*"):
     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 
    101 def 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 
    107 def 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