mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Introduce support for documenting which C API elements are not part of the stable/limited API.
This commit is contained in:
		
							parent
							
								
									5db7c54f96
								
							
						
					
					
						commit
						5c01d99c12
					
				
					 5 changed files with 152 additions and 30 deletions
				
			
		|  | @ -13,6 +13,7 @@ document the API functions in detail. | ||||||
|    :maxdepth: 2 |    :maxdepth: 2 | ||||||
| 
 | 
 | ||||||
|    intro.rst |    intro.rst | ||||||
|  |    stable.rst | ||||||
|    veryhigh.rst |    veryhigh.rst | ||||||
|    refcounting.rst |    refcounting.rst | ||||||
|    exceptions.rst |    exceptions.rst | ||||||
|  | @ -22,5 +23,4 @@ document the API functions in detail. | ||||||
|    init.rst |    init.rst | ||||||
|    memory.rst |    memory.rst | ||||||
|    objimpl.rst |    objimpl.rst | ||||||
|    stable.rst |  | ||||||
|    apiabiversion.rst |    apiabiversion.rst | ||||||
|  |  | ||||||
|  | @ -6,36 +6,33 @@ | ||||||
| Stable Application Binary Interface | Stable Application Binary Interface | ||||||
| *********************************** | *********************************** | ||||||
| 
 | 
 | ||||||
| Traditionally, the C API of Python will change with every release. | Traditionally, the C API of Python will change with every release.  Most changes | ||||||
| Most changes will be source-compatible, typically by only adding API, | will be source-compatible, typically by only adding API, rather than changing | ||||||
| rather than changing existing API or removing API (although some | existing API or removing API (although some interfaces do get removed after | ||||||
| interfaces do get removed after being deprecated first). | being deprecated first). | ||||||
| 
 | 
 | ||||||
| Unfortunately, the API compatibility does not extend to binary | Unfortunately, the API compatibility does not extend to binary compatibility | ||||||
| compatibility (the ABI). The reason is primarily the evolution of | (the ABI). The reason is primarily the evolution of struct definitions, where | ||||||
| struct definitions, where addition of a new field, or changing | addition of a new field, or changing the type of a field, might not break the | ||||||
| the type of a field, might not break the API, but can break the ABI. | API, but can break the ABI.  As a consequence, extension modules need to be | ||||||
| As a consequence, extension modules need to be recompiled for | recompiled for every Python release (although an exception is possible on Unix | ||||||
| every Python release (although an exception is possible on Unix | when none of the affected interfaces are used). In addition, on Windows, | ||||||
| when none of the affected interfaces are used). In addition, on | extension modules link with a specific pythonXY.dll and need to be recompiled to | ||||||
| Windows, extension modules link with a specific pythonXY.dll and | link with a newer one. | ||||||
| need to be recompiled to link with a newer one. |  | ||||||
| 
 | 
 | ||||||
| Since Python 3.2, a subset of the API has been declared to guarantee | Since Python 3.2, a subset of the API has been declared to guarantee a stable | ||||||
| a stable ABI. Extension modules wishing to use this API need to define | ABI. Extension modules wishing to use this API (called "limited API") need to | ||||||
| ``Py_LIMITED_API``. A number of interpreter details then become hidden | define ``Py_LIMITED_API``. A number of interpreter details then become hidden | ||||||
| from the extension module; in return, a module is built that works | from the extension module; in return, a module is built that works on any 3.x | ||||||
| on any 3.x version (x>=2) without recompilation. | version (x>=2) without recompilation. | ||||||
| 
 | 
 | ||||||
| In some cases, the stable ABI needs to be extended with new functions. | In some cases, the stable ABI needs to be extended with new functions. | ||||||
| Extension modules wishing to use these new APIs need to set | Extension modules wishing to use these new APIs need to set ``Py_LIMITED_API`` | ||||||
| ``Py_LIMITED_API`` to the ``PY_VERSION_HEX`` value (see | to the ``PY_VERSION_HEX`` value (see :ref:`apiabiversion`) of the minimum Python | ||||||
| :ref:`apiabiversion`) of the minimum Python version they want to | version they want to support (e.g. ``0x03030000`` for Python 3.3). Such modules | ||||||
| support (e.g. ``0x03030000`` for Python 3.3). Such modules will work | will work on all subsequent Python releases, but fail to load (because of | ||||||
| on all subsequent Python releases, but fail to load (because of |  | ||||||
| missing symbols) on the older releases. | missing symbols) on the older releases. | ||||||
| 
 | 
 | ||||||
| As of Python 3.2, the set of functions available to the limited API | As of Python 3.2, the set of functions available to the limited API is | ||||||
| is documented in PEP 384. | documented in PEP 384.  In the C API documentation, API elements that are not | ||||||
| 
 | part of the limited API are marked as "Not part of the limited API." | ||||||
| .. XXX copy exact list here? Into each functions definition? |  | ||||||
|  |  | ||||||
|  | @ -12,8 +12,8 @@ | ||||||
| # General configuration | # General configuration | ||||||
| # --------------------- | # --------------------- | ||||||
| 
 | 
 | ||||||
| extensions = ['sphinx.ext.refcounting', 'sphinx.ext.coverage', | extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest', | ||||||
|               'sphinx.ext.doctest', 'pyspecific'] |               'pyspecific', 'c_annotations'] | ||||||
| templates_path = ['tools/sphinxext'] | templates_path = ['tools/sphinxext'] | ||||||
| 
 | 
 | ||||||
| # General substitutions. | # General substitutions. | ||||||
|  |  | ||||||
							
								
								
									
										117
									
								
								Doc/tools/sphinxext/c_annotations.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								Doc/tools/sphinxext/c_annotations.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | ||||||
|  | # -*- 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['notlimited']: | ||||||
|  |                 node.insert(0, nodes.emphasis(' Not part of the stable API.', | ||||||
|  |                                               ' Not part of the stable API.', | ||||||
|  |                                               classes=['notlimited'])) | ||||||
|  |             if par['objtype'] != 'function': | ||||||
|  |                 continue | ||||||
|  |             if not par[0].has_key('names') or not par[0]['names']: | ||||||
|  |                 continue | ||||||
|  |             entry = self.get(par[0]['names'][0]) | ||||||
|  |             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, | ||||||
|  |         'notlimited': directives.flag, | ||||||
|  |     } | ||||||
|  |     old_handle_signature = CObject.handle_signature | ||||||
|  |     def new_handle_signature(self, sig, signode): | ||||||
|  |         signode.parent['notlimited'] = 'notlimited' in self.options | ||||||
|  |         return old_handle_signature(self, sig, signode) | ||||||
|  |     CObject.handle_signature = new_handle_signature | ||||||
|  | @ -168,3 +168,11 @@ div.footer { | ||||||
| div.footer a:hover { | div.footer a:hover { | ||||||
|     color: #0095C4; |     color: #0095C4; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .refcount { | ||||||
|  |     color: #060; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .notlimited { | ||||||
|  |     color: #922; | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Georg Brandl
						Georg Brandl