| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  | # -*- 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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-30 22:17:41 +02:00
										 |  |  |     :copyright: Copyright 2007-2014 by Georg Brandl. | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |     :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 | 
					
						
							| 
									
										
										
										
											2013-10-12 22:55:34 +02:00
										 |  |  |             if par['stableabi']: | 
					
						
							|  |  |  |                 node.insert(0, nodes.emphasis(' Part of the stable ABI.', | 
					
						
							|  |  |  |                                               ' Part of the stable ABI.', | 
					
						
							|  |  |  |                                               classes=['stableabi'])) | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |             if par['objtype'] != 'function': | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if not par[0].has_key('names') or not par[0]['names']: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2014-04-17 18:29:01 -04:00
										 |  |  |             name = par[0]['names'][0] | 
					
						
							|  |  |  |             if name.startswith("c."): | 
					
						
							|  |  |  |                 name = name[2:] | 
					
						
							|  |  |  |             entry = self.get(name) | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |             if not entry: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2018-12-20 09:33:58 +02:00
										 |  |  |             elif not entry.result_type.endswith("Object*"): | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |                 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, | 
					
						
							| 
									
										
										
										
											2013-10-12 22:55:34 +02:00
										 |  |  |         'stableabi': directives.flag, | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     old_handle_signature = CObject.handle_signature | 
					
						
							|  |  |  |     def new_handle_signature(self, sig, signode): | 
					
						
							| 
									
										
										
										
											2013-10-12 22:55:34 +02:00
										 |  |  |         signode.parent['stableabi'] = 'stableabi' in self.options | 
					
						
							| 
									
										
										
										
											2013-10-12 19:54:30 +02:00
										 |  |  |         return old_handle_signature(self, sig, signode) | 
					
						
							|  |  |  |     CObject.handle_signature = new_handle_signature | 
					
						
							| 
									
										
										
										
											2014-09-30 22:17:41 +02:00
										 |  |  |     return {'version': '1.0', 'parallel_read_safe': True} |