mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-43795: PEP 652 user documentation (GH-25668)
- Reformat the C API and ABI Versioning page (and extend/clarify a bit) - Rewrite the stable ABI docs into a general text on C API Compatibility - Add a list of Limited API contents, and notes for the individual items. - Replace `Include/README.rst` with a link to a devguide page with the same info
This commit is contained in:
		
							parent
							
								
									d1b81574ed
								
							
						
					
					
						commit
						b05955d6f5
					
				
					 7 changed files with 1180 additions and 1097 deletions
				
			
		|  | @ -6,34 +6,57 @@ | ||||||
| API and ABI Versioning | API and ABI Versioning | ||||||
| *********************** | *********************** | ||||||
| 
 | 
 | ||||||
| ``PY_VERSION_HEX`` is the Python version number encoded in a single integer. | CPython exposes its version number in the following macros. | ||||||
|  | Note that these correspond to the version code is **built** with, | ||||||
|  | not necessarily the version used at **run time**. | ||||||
| 
 | 
 | ||||||
| For example if the ``PY_VERSION_HEX`` is set to ``0x030401a2``, the underlying | See :ref:`stable` for a discussion of API and ABI stability across versions. | ||||||
| version information can be found by treating it as a 32 bit number in |  | ||||||
| the following manner: |  | ||||||
| 
 | 
 | ||||||
|    +-------+-------------------------+------------------------------------------------+ | .. c:macro:: PY_MAJOR_VERSION | ||||||
|    | Bytes | Bits (big endian order) | Meaning                                        | | 
 | ||||||
|    +=======+=========================+================================================+ |    The ``3`` in ``3.4.1a2``. | ||||||
|    | ``1`` |       ``1-8``           |  ``PY_MAJOR_VERSION`` (the ``3`` in            | | 
 | ||||||
|    |       |                         |  ``3.4.1a2``)                                  | | .. c:macro:: PY_MINOR_VERSION | ||||||
|    +-------+-------------------------+------------------------------------------------+ | 
 | ||||||
|    | ``2`` |       ``9-16``          |  ``PY_MINOR_VERSION`` (the ``4`` in            | |    The ``4`` in ``3.4.1a2``. | ||||||
|    |       |                         |  ``3.4.1a2``)                                  | | 
 | ||||||
|    +-------+-------------------------+------------------------------------------------+ | .. c:macro:: PY_MICRO_VERSION | ||||||
|    | ``3`` |       ``17-24``         |  ``PY_MICRO_VERSION`` (the ``1`` in            | | 
 | ||||||
|    |       |                         |  ``3.4.1a2``)                                  | |    The ``1`` in ``3.4.1a2``. | ||||||
|    +-------+-------------------------+------------------------------------------------+ | 
 | ||||||
|    | ``4`` |       ``25-28``         |  ``PY_RELEASE_LEVEL`` (``0xA`` for alpha,      | | .. c:macro:: PY_RELEASE_LEVEL | ||||||
|    |       |                         |  ``0xB`` for beta, ``0xC`` for release         | | 
 | ||||||
|    |       |                         |  candidate and ``0xF`` for final), in this     | |    The ``a`` in ``3.4.1a2``. | ||||||
|    |       |                         |  case it is alpha.                             | |    This can be ``0xA`` for alpha, ``0xB`` for beta, ``0xC`` for release | ||||||
|    +-------+-------------------------+------------------------------------------------+ |    candidate or ``0xF`` for final. | ||||||
|    |       |       ``29-32``         |  ``PY_RELEASE_SERIAL`` (the ``2`` in           | | 
 | ||||||
|    |       |                         |  ``3.4.1a2``, zero for final releases)         | | .. c:macro:: PY_RELEASE_SERIAL | ||||||
|    +-------+-------------------------+------------------------------------------------+ | 
 | ||||||
|  |    The ``2`` in ``3.4.1a2``. Zero for final releases. | ||||||
|  | 
 | ||||||
|  | .. c:macro:: PY_VERSION_HEX | ||||||
|  | 
 | ||||||
|  |    The Python version number encoded in a single integer. | ||||||
|  | 
 | ||||||
|  |    The underlying version information can be found by treating it as a 32 bit | ||||||
|  |    number in the following manner: | ||||||
|  | 
 | ||||||
|  |    +-------+-------------------------+-------------------------+--------------------------+ | ||||||
|  |    | Bytes | Bits (big endian order) | Meaning                 | Value for ``3.4.1a2``    | | ||||||
|  |    +=======+=========================+=========================+==========================+ | ||||||
|  |    |   1   |         1-8             |  ``PY_MAJOR_VERSION``   | ``0x03``                 | | ||||||
|  |    +-------+-------------------------+-------------------------+--------------------------+ | ||||||
|  |    |   2   |         9-16            |  ``PY_MINOR_VERSION``   | ``0x04``                 | | ||||||
|  |    +-------+-------------------------+-------------------------+--------------------------+ | ||||||
|  |    |   3   |         17-24           |  ``PY_MICRO_VERSION``   | ``0x01``                 | | ||||||
|  |    +-------+-------------------------+-------------------------+--------------------------+ | ||||||
|  |    |   4   |         25-28           |  ``PY_RELEASE_LEVEL``   | ``0xA``                  | | ||||||
|  |    +       +-------------------------+-------------------------+--------------------------+ | ||||||
|  |    |       |         29-32           |  ``PY_RELEASE_SERIAL``  | ``0x2``                  | | ||||||
|  |    +-------+-------------------------+-------------------------+--------------------------+ | ||||||
|  | 
 | ||||||
|  |    Thus ``3.4.1a2`` is hexversion ``0x030401a2`` and ``3.10.0`` is | ||||||
|  |    hexversion ``0x030a00f0``. | ||||||
| 
 | 
 | ||||||
| Thus ``3.4.1a2`` is hexversion ``0x030401a2``. |  | ||||||
| 
 | 
 | ||||||
| All the given macros are defined in :source:`Include/patchlevel.h`. | All the given macros are defined in :source:`Include/patchlevel.h`. | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -2,37 +2,157 @@ | ||||||
| 
 | 
 | ||||||
| .. _stable: | .. _stable: | ||||||
| 
 | 
 | ||||||
| *********************************** | *************** | ||||||
|  | C API Stability | ||||||
|  | *************** | ||||||
|  | 
 | ||||||
|  | Python's C API is covered by the Backwards Compatibility Policy, :pep:`387`. | ||||||
|  | While the C API will change with every minor release (e.g. from 3.9 to 3.10), | ||||||
|  | most changes will be source-compatible, typically by only adding new API. | ||||||
|  | Changing existing API or removing API is only done after a deprecation period | ||||||
|  | or to fix serious issues. | ||||||
|  | 
 | ||||||
|  | CPython's Application Binary Interface (ABI) is forward- and | ||||||
|  | backwards-compatible across a minor release (if these are compiled the same | ||||||
|  | way; see :ref:`stable-abi-platform` below). | ||||||
|  | So, code compiled for Python 3.10.0 will work on 3.10.8 and vice versa, | ||||||
|  | but will need to be compiled separately for 3.9.x and 3.10.x. | ||||||
|  | 
 | ||||||
|  | Names prefixed by an underscore, such as ``_Py_InternalState``, | ||||||
|  | are private API that can change without notice even in patch releases. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Stable Application Binary Interface | Stable Application Binary Interface | ||||||
| *********************************** | =================================== | ||||||
| 
 | 
 | ||||||
| Traditionally, the C API of Python will change with every release.  Most changes | Python 3.2 introduced the *Limited API*, a subset of Python's C API. | ||||||
| will be source-compatible, typically by only adding API, rather than changing | Extensions that only use the Limited API can be | ||||||
| existing API or removing API (although some interfaces do get removed after | compiled once and work with multiple versions of Python. | ||||||
| being deprecated first). | Contents of the Limited API are :ref:`listed below <stable-abi-list>`. | ||||||
| 
 | 
 | ||||||
| Unfortunately, the API compatibility does not extend to binary compatibility | To enable this, Python provides a *Stable ABI*: a set of symbols that will | ||||||
| (the ABI). The reason is primarily the evolution of struct definitions, where | remain compatible across Python 3.x versions. The Stable ABI contains symbols | ||||||
| addition of a new field, or changing the type of a field, might not break the | exposed in the Limited API, but also other ones – for example, functions | ||||||
| API, but can break the ABI.  As a consequence, extension modules need to be | necessary to support older versions of the Limited API. | ||||||
| recompiled for every Python release (although an exception is possible on Unix |  | ||||||
| when none of the affected interfaces are used). In addition, on Windows, |  | ||||||
| extension modules link with a specific pythonXY.dll and need to be recompiled to |  | ||||||
| link with a newer one. |  | ||||||
| 
 | 
 | ||||||
| Since Python 3.2, a subset of the API has been declared to guarantee a stable | (For simplicity, this document talks about *extensions*, but the Limited API | ||||||
| ABI. Extension modules wishing to use this API (called "limited API") need to | and Stable ABI work the same way for all uses of the API – for example, | ||||||
| define ``Py_LIMITED_API``. A number of interpreter details then become hidden | embedding Python.) | ||||||
| from the extension module; in return, a module is built that works on any 3.x |  | ||||||
| version (x>=2) without recompilation. |  | ||||||
| 
 | 
 | ||||||
| In some cases, the stable ABI needs to be extended with new functions. | .. c:macro:: Py_LIMITED_API | ||||||
| Extension modules wishing to use these new APIs need to set ``Py_LIMITED_API`` |  | ||||||
| to the ``PY_VERSION_HEX`` value (see :ref:`apiabiversion`) of the minimum Python |  | ||||||
| version they want to support (e.g. ``0x03030000`` for Python 3.3). Such modules |  | ||||||
| will work on all subsequent Python releases, but fail to load (because of |  | ||||||
| missing symbols) on the older releases. |  | ||||||
| 
 | 
 | ||||||
| As of Python 3.2, the set of functions available to the limited API is |    Define this macro ``Py_LIMITED_API`` before including ``Python.h`` to | ||||||
| documented in :pep:`384`.  In the C API documentation, API elements that are not |    opt in to only use the Limited API. | ||||||
| part of the limited API are marked as "Not part of the limited API." | 
 | ||||||
|  |    Defining ``Py_LIMITED_API`` to ``3`` will limit the available API so that | ||||||
|  |    the extension will work without recompilation with all Python 3.x releases | ||||||
|  |    (x>=2) on the particular  :ref:`platform <stable-abi-platform>`. | ||||||
|  | 
 | ||||||
|  |    Defining ``Py_LIMITED_API`` to a value of :c:data:`PY_VERSION_HEX` will | ||||||
|  |    limit the available API so that the extension will work without | ||||||
|  |    recompilation with all Python 3 releases from the specified one. | ||||||
|  |    This will allow using additional API introduced up to this version, | ||||||
|  |    but the extension will lose compatibility with earlier Python versions. | ||||||
|  |    Rather than using the ``PY_VERSION_HEX`` macro directly, hardcode a minimum | ||||||
|  |    minor version (e.g. ``0x030A0000`` for Python 3.10) for stability when | ||||||
|  |    compiling with future Python versions. | ||||||
|  | 
 | ||||||
|  | On Windows, extensions that use the Stable ABI should be linked against | ||||||
|  | ``python3.dll`` rather than a version-specific library such as | ||||||
|  | ``python39.dll``. | ||||||
|  | 
 | ||||||
|  | On some platforms, Python will look for and load shared library files named | ||||||
|  | with the ``abi3`` tag (e.g. ``mymodule.abi3.so``). | ||||||
|  | It does not check if such extensions conform to a Stable ABI. | ||||||
|  | The user (or their packaging tools) need to ensure that, for example, | ||||||
|  | extensions built with the 3.10+ Limited API are not installed for lower | ||||||
|  | versions of Python. | ||||||
|  | 
 | ||||||
|  | All functions in the Stable ABI are present as functions in Python's shared | ||||||
|  | library, not solely as macros. This makes them usable from languages that don't | ||||||
|  | use the C preprocessor. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Limited API Scope and Performance | ||||||
|  | --------------------------------- | ||||||
|  | 
 | ||||||
|  | The goal for the Limited API is to allow everything that is possible with the | ||||||
|  | full C API, but possibly with a performance penalty. | ||||||
|  | 
 | ||||||
|  | For example, while :c:func:`PyList_GetItem` is available, its “unsafe” macro | ||||||
|  | variant :c:func:`PyList_GET_ITEM` is not. | ||||||
|  | The macro can be faster because it can rely on version-specific implementation | ||||||
|  | details of the list object. | ||||||
|  | 
 | ||||||
|  | Without ``Py_LIMITED_API`` defined, some C API functions are inlined or | ||||||
|  | replaced by macros. | ||||||
|  | Defining ``Py_LIMITED_API`` disables this inlining, allowing stability as | ||||||
|  | Python's data structures are improved, but possibly reducing performance. | ||||||
|  | 
 | ||||||
|  | By leaving out the ``Py_LIMITED_API`` definition, it is possible to compile | ||||||
|  | a Limited API extension with a version-specific ABI. This can improve | ||||||
|  | performance for that Python version, but will limit compatibility. | ||||||
|  | Compiling with ``Py_LIMITED_API`` will then yield an extension that can be | ||||||
|  | distributed where a version-specific one is not available – for example, | ||||||
|  | for prereleases of an upcoming Python version. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Limited API Caveats | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | Note that compiling with ``Py_LIMITED_API`` is *not* a complete guarantee that | ||||||
|  | code conforms to the Limited API or the Stable ABI. ``Py_LIMITED_API`` only | ||||||
|  | covers definitions, but an API also includes other issues, such as expected | ||||||
|  | semantics. | ||||||
|  | 
 | ||||||
|  | One issue that ``Py_LIMITED_API`` does not guard against is calling a function | ||||||
|  | with arguments that are invalid in a lower Python version. | ||||||
|  | For example, consider a function that starts accepting ``NULL`` for an | ||||||
|  | argument. In Python 3.9, ``NULL`` now selects a default behavior, but in | ||||||
|  | Python 3.8, the argument will be used directly, causing a ``NULL`` dereference | ||||||
|  | and crash. A similar argument works for fields of structs. | ||||||
|  | 
 | ||||||
|  | Another issue is that some struct fields are currently not hidden when | ||||||
|  | ``Py_LIMITED_API`` is defined, even though they're part of the Limited API. | ||||||
|  | 
 | ||||||
|  | For these reasons, we recommend testing an extension with *all* minor Python | ||||||
|  | versions it supports, and preferably to build with the *lowest* such version. | ||||||
|  | 
 | ||||||
|  | We also recommend reviewing documentation of all used API to check | ||||||
|  | if it is explicitly part of the Limited API. Even with ``Py_LIMITED_API`` | ||||||
|  | defined, a few private declarations are exposed for technical reasons (or | ||||||
|  | even unintentionally, as bugs). | ||||||
|  | 
 | ||||||
|  | Also note that the Limited API is not necessarily stable: compiling with | ||||||
|  | ``Py_LIMITED_API`` with Python 3.8 means that the extension will | ||||||
|  | run with Python 3.12, but it will not necessarily *compile* with Python 3.12. | ||||||
|  | In particular, parts of the Limited API may be deprecated and removed, | ||||||
|  | provided that the Stable ABI stays stable. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .. _stable-abi-platform: | ||||||
|  | 
 | ||||||
|  | Platform Considerations | ||||||
|  | ======================= | ||||||
|  | 
 | ||||||
|  | ABI stability depends not only on Python, but also on the compiler used, | ||||||
|  | lower-level libraries and compiler options. For the purposes of the Stable ABI, | ||||||
|  | these details define a “platform”. They usually depend on the OS | ||||||
|  | type and processor architecture | ||||||
|  | 
 | ||||||
|  | It is the responsibility of each particular distributor of Python | ||||||
|  | to ensure that all Python versions on a particular platform are built | ||||||
|  | in a way that does not break the Stable ABI. | ||||||
|  | This is the case with Windows and macOS releases from ``python.org`` and many | ||||||
|  | third-party distributors. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .. _stable-abi-list: | ||||||
|  | 
 | ||||||
|  | Contents of Limited API | ||||||
|  | ======================= | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Currently, the Limited API includes the following items: | ||||||
|  | 
 | ||||||
|  | .. limited-api-list:: | ||||||
|  |  | ||||||
|  | @ -225,8 +225,9 @@ | ||||||
| # Options for extensions | # Options for extensions | ||||||
| # ---------------------- | # ---------------------- | ||||||
| 
 | 
 | ||||||
| # Relative filename of the reference count data file. | # Relative filename of the data files | ||||||
| refcount_file = 'data/refcounts.dat' | refcount_file = 'data/refcounts.dat' | ||||||
|  | stable_abi_file = 'data/stable_abi.dat' | ||||||
| 
 | 
 | ||||||
| # Sphinx 2 and Sphinx 3 compatibility | # Sphinx 2 and Sphinx 3 compatibility | ||||||
| # ----------------------------------- | # ----------------------------------- | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -10,8 +10,10 @@ | ||||||
| 
 | 
 | ||||||
|     * stable API annotations |     * stable API annotations | ||||||
| 
 | 
 | ||||||
|     Usage: Set the `refcount_file` config value to the path to the reference |     Usage: | ||||||
|  |     * Set the `refcount_file` config value to the path to the reference | ||||||
|     count data file. |     count data file. | ||||||
|  |     * Set the `stable_abi_file` config value to the path to stable ABI list. | ||||||
| 
 | 
 | ||||||
|     :copyright: Copyright 2007-2014 by Georg Brandl. |     :copyright: Copyright 2007-2014 by Georg Brandl. | ||||||
|     :license: Python license. |     :license: Python license. | ||||||
|  | @ -20,11 +22,23 @@ | ||||||
| from os import path | from os import path | ||||||
| from docutils import nodes | from docutils import nodes | ||||||
| from docutils.parsers.rst import directives | from docutils.parsers.rst import directives | ||||||
|  | from docutils.parsers.rst import Directive | ||||||
|  | from docutils.statemachine import StringList | ||||||
|  | import csv | ||||||
| 
 | 
 | ||||||
| from sphinx import addnodes | from sphinx import addnodes | ||||||
| from sphinx.domains.c import CObject | from sphinx.domains.c import CObject | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | REST_ROLE_MAP = { | ||||||
|  |     'function': 'func', | ||||||
|  |     'var': 'data', | ||||||
|  |     'type': 'type', | ||||||
|  |     'macro': 'macro', | ||||||
|  |     'type': 'type', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class RCEntry: | class RCEntry: | ||||||
|     def __init__(self, name): |     def __init__(self, name): | ||||||
|         self.name = name |         self.name = name | ||||||
|  | @ -33,12 +47,10 @@ def __init__(self, name): | ||||||
|         self.result_refs = None |         self.result_refs = None | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Annotations(dict): | class Annotations: | ||||||
|     @classmethod |     def __init__(self, refcount_filename, stable_abi_file): | ||||||
|     def fromfile(cls, filename): |         self.refcount_data = {} | ||||||
|         d = cls() |         with open(refcount_filename, 'r') as fp: | ||||||
|         fp = open(filename, 'r') |  | ||||||
|         try: |  | ||||||
|             for line in fp: |             for line in fp: | ||||||
|                 line = line.strip() |                 line = line.strip() | ||||||
|                 if line[:1] in ("", "#"): |                 if line[:1] in ("", "#"): | ||||||
|  | @ -50,9 +62,9 @@ def fromfile(cls, filename): | ||||||
|                 function, type, arg, refcount, comment = parts |                 function, type, arg, refcount, comment = parts | ||||||
|                 # Get the entry, creating it if needed: |                 # Get the entry, creating it if needed: | ||||||
|                 try: |                 try: | ||||||
|                     entry = d[function] |                     entry = self.refcount_data[function] | ||||||
|                 except KeyError: |                 except KeyError: | ||||||
|                     entry = d[function] = RCEntry(function) |                     entry = self.refcount_data[function] = RCEntry(function) | ||||||
|                 if not refcount or refcount == "null": |                 if not refcount or refcount == "null": | ||||||
|                     refcount = None |                     refcount = None | ||||||
|                 else: |                 else: | ||||||
|  | @ -64,27 +76,58 @@ def fromfile(cls, filename): | ||||||
|                 else: |                 else: | ||||||
|                     entry.result_type = type |                     entry.result_type = type | ||||||
|                     entry.result_refs = refcount |                     entry.result_refs = refcount | ||||||
|         finally: | 
 | ||||||
|             fp.close() |         self.stable_abi_data = {} | ||||||
|         return d |         with open(stable_abi_file, 'r') as fp: | ||||||
|  |             for record in csv.DictReader(fp): | ||||||
|  |                 role = record['role'] | ||||||
|  |                 name = record['name'] | ||||||
|  |                 self.stable_abi_data[name] = record | ||||||
| 
 | 
 | ||||||
|     def add_annotations(self, app, doctree): |     def add_annotations(self, app, doctree): | ||||||
|         for node in doctree.traverse(addnodes.desc_content): |         for node in doctree.traverse(addnodes.desc_content): | ||||||
|             par = node.parent |             par = node.parent | ||||||
|             if par['domain'] != 'c': |             if par['domain'] != 'c': | ||||||
|                 continue |                 continue | ||||||
|             if par['stableabi']: |  | ||||||
|                 node.insert(0, nodes.emphasis(' Part of the stable ABI.', |  | ||||||
|                                               ' Part of the stable ABI.', |  | ||||||
|                                               classes=['stableabi'])) |  | ||||||
|             if par['objtype'] != 'function': |  | ||||||
|                 continue |  | ||||||
|             if not par[0].has_key('ids') or not par[0]['ids']: |             if not par[0].has_key('ids') or not par[0]['ids']: | ||||||
|                 continue |                 continue | ||||||
|             name = par[0]['ids'][0] |             name = par[0]['ids'][0] | ||||||
|             if name.startswith("c."): |             if name.startswith("c."): | ||||||
|                 name = name[2:] |                 name = name[2:] | ||||||
|             entry = self.get(name) | 
 | ||||||
|  |             objtype = par['objtype'] | ||||||
|  | 
 | ||||||
|  |             # Stable ABI annotation. These have two forms: | ||||||
|  |             #   Part of the [Stable ABI](link). | ||||||
|  |             #   Part of the [Stable ABI](link) since version X.Y. | ||||||
|  |             record = self.stable_abi_data.get(name) | ||||||
|  |             if record: | ||||||
|  |                 if record['role'] != objtype: | ||||||
|  |                     raise ValueError( | ||||||
|  |                         f"Object type mismatch in limited API annotation " | ||||||
|  |                         f"for {name}: {record['role']!r} != {objtype!r}") | ||||||
|  |                 stable_added = record['added'] | ||||||
|  |                 message = ' Part of the ' | ||||||
|  |                 emph_node = nodes.emphasis(message, message, | ||||||
|  |                                            classes=['stableabi']) | ||||||
|  |                 ref_node = addnodes.pending_xref( | ||||||
|  |                     'Stable ABI', refdomain="std", reftarget='stable', | ||||||
|  |                     reftype='ref', refexplicit="False") | ||||||
|  |                 ref_node += nodes.Text('Stable ABI') | ||||||
|  |                 emph_node += ref_node | ||||||
|  |                 if record['ifdef_note']: | ||||||
|  |                     emph_node += nodes.Text(' ' + record['ifdef_note']) | ||||||
|  |                 if stable_added == '3.2': | ||||||
|  |                     # Stable ABI was introduced in 3.2. | ||||||
|  |                     emph_node += nodes.Text('.') | ||||||
|  |                 else: | ||||||
|  |                     emph_node += nodes.Text(f' since version {stable_added}.') | ||||||
|  |                 node.insert(0, emph_node) | ||||||
|  | 
 | ||||||
|  |             # Return value annotation | ||||||
|  |             if objtype != 'function': | ||||||
|  |                 continue | ||||||
|  |             entry = self.refcount_data.get(name) | ||||||
|             if not entry: |             if not entry: | ||||||
|                 continue |                 continue | ||||||
|             elif not entry.result_type.endswith("Object*"): |             elif not entry.result_type.endswith("Object*"): | ||||||
|  | @ -99,13 +142,36 @@ def add_annotations(self, app, doctree): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def init_annotations(app): | def init_annotations(app): | ||||||
|     refcounts = Annotations.fromfile( |     annotations = Annotations( | ||||||
|         path.join(app.srcdir, app.config.refcount_file)) |         path.join(app.srcdir, app.config.refcount_file), | ||||||
|     app.connect('doctree-read', refcounts.add_annotations) |         path.join(app.srcdir, app.config.stable_abi_file), | ||||||
|  |     ) | ||||||
|  |     app.connect('doctree-read', annotations.add_annotations) | ||||||
|  | 
 | ||||||
|  |     class LimitedAPIList(Directive): | ||||||
|  | 
 | ||||||
|  |         has_content = False | ||||||
|  |         required_arguments = 0 | ||||||
|  |         optional_arguments = 0 | ||||||
|  |         final_argument_whitespace = True | ||||||
|  | 
 | ||||||
|  |         def run(self): | ||||||
|  |             content = [] | ||||||
|  |             for record in annotations.stable_abi_data.values(): | ||||||
|  |                 role = REST_ROLE_MAP[record['role']] | ||||||
|  |                 name = record['name'] | ||||||
|  |                 content.append(f'* :c:{role}:`{name}`') | ||||||
|  | 
 | ||||||
|  |             pnode = nodes.paragraph() | ||||||
|  |             self.state.nested_parse(StringList(content), 0, pnode) | ||||||
|  |             return [pnode] | ||||||
|  | 
 | ||||||
|  |     app.add_directive('limited-api-list', LimitedAPIList) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def setup(app): | def setup(app): | ||||||
|     app.add_config_value('refcount_file', '', True) |     app.add_config_value('refcount_file', '', True) | ||||||
|  |     app.add_config_value('stable_abi_file', '', True) | ||||||
|     app.connect('builder-inited', init_annotations) |     app.connect('builder-inited', init_annotations) | ||||||
| 
 | 
 | ||||||
|     # monkey-patch C object... |     # monkey-patch C object... | ||||||
|  |  | ||||||
|  | @ -3,66 +3,10 @@ The Python C API | ||||||
| 
 | 
 | ||||||
| The C API is divided into three sections: | The C API is divided into three sections: | ||||||
| 
 | 
 | ||||||
| 1. ``Include/`` | 1. ``Include/``: Limited API | ||||||
| 2. ``Include/cpython/`` | 2. ``Include/cpython/``: CPython implementation details | ||||||
| 3. ``Include/internal/`` | 3. ``Include/internal/``: The internal API | ||||||
| 
 | 
 | ||||||
|  | Information on changing the C API is available `in the developer guide`_ | ||||||
| 
 | 
 | ||||||
| Include: Limited API | .. _in the developer guide: https://devguide.python.org/c-api/ | ||||||
| ==================== |  | ||||||
| 
 |  | ||||||
| ``Include/``, excluding the ``cpython`` and ``internal`` subdirectories, |  | ||||||
| contains the public Limited API (Application Programming Interface). |  | ||||||
| The Limited API is a subset of the C API, designed to guarantee ABI |  | ||||||
| stability across Python 3 versions, and is defined in :pep:`384`. |  | ||||||
| 
 |  | ||||||
| Guidelines for expanding the Limited API: |  | ||||||
| 
 |  | ||||||
| - Functions *must not* steal references |  | ||||||
| - Functions *must not* return borrowed references |  | ||||||
| - Functions returning references *must* return a strong reference |  | ||||||
| - Macros should not expose implementation details |  | ||||||
| - Please start a public discussion before expanding the API |  | ||||||
| - Functions or macros with a ``_Py`` prefix do not belong in ``Include/``. |  | ||||||
| 
 |  | ||||||
| It is possible to add a function or macro to the Limited API from a |  | ||||||
| given Python version.  For example, to add a function to the Limited API |  | ||||||
| from Python 3.10 and onwards, wrap it with |  | ||||||
| ``#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000``. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Include/cpython: CPython implementation details |  | ||||||
| =============================================== |  | ||||||
| 
 |  | ||||||
| ``Include/cpython/`` contains the public API that is excluded from the |  | ||||||
| Limited API and the Stable ABI. |  | ||||||
| 
 |  | ||||||
| Guidelines for expanding the public API: |  | ||||||
| 
 |  | ||||||
| - Functions *must not* steal references |  | ||||||
| - Functions *must not* return borrowed references |  | ||||||
| - Functions returning references *must* return a strong reference |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Include/internal: The internal API |  | ||||||
| ================================== |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| With PyAPI_FUNC or PyAPI_DATA |  | ||||||
| ----------------------------- |  | ||||||
| 
 |  | ||||||
| Functions or structures in ``Include/internal/`` defined with |  | ||||||
| ``PyAPI_FUNC`` or ``PyAPI_DATA`` are internal functions which are |  | ||||||
| exposed only for specific use cases like debuggers and profilers. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| With the extern keyword |  | ||||||
| ----------------------- |  | ||||||
| 
 |  | ||||||
| Functions in ``Include/internal/`` defined with the ``extern`` keyword |  | ||||||
| *must not and can not* be used outside the CPython code base.  Only |  | ||||||
| built-in stdlib extensions (built with the ``Py_BUILD_CORE_BUILTIN`` |  | ||||||
| macro defined) can use such functions. |  | ||||||
| 
 |  | ||||||
| When in doubt, new internal C functions should be defined in |  | ||||||
| ``Include/internal`` using the ``extern`` keyword. |  | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| import os.path | import os.path | ||||||
| import io | import io | ||||||
| import re | import re | ||||||
|  | import csv | ||||||
| 
 | 
 | ||||||
| MISSING = object() | MISSING = object() | ||||||
| 
 | 
 | ||||||
|  | @ -45,6 +46,11 @@ | ||||||
| MACOS = (sys.platform == "darwin") | MACOS = (sys.platform == "darwin") | ||||||
| UNIXY = MACOS or (sys.platform == "linux")  # XXX should this be "not Windows"? | UNIXY = MACOS or (sys.platform == "linux")  # XXX should this be "not Windows"? | ||||||
| 
 | 
 | ||||||
|  | IFDEF_DOC_NOTES = { | ||||||
|  |     'MS_WINDOWS': 'on Windows', | ||||||
|  |     'HAVE_FORK': 'on platforms with fork()', | ||||||
|  |     'USE_STACKCHECK': 'on platforms with USE_STACKCHECK', | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| # The stable ABI manifest (Misc/stable_abi.txt) exists only to fill the | # The stable ABI manifest (Misc/stable_abi.txt) exists only to fill the | ||||||
| # following dataclasses. | # following dataclasses. | ||||||
|  | @ -227,16 +233,31 @@ def sort_key(item): | ||||||
|             key=sort_key): |             key=sort_key): | ||||||
|         write(f'EXPORT_DATA({item.name})') |         write(f'EXPORT_DATA({item.name})') | ||||||
| 
 | 
 | ||||||
|  | REST_ROLES = { | ||||||
|  |     'function': 'function', | ||||||
|  |     'data': 'var', | ||||||
|  |     'struct': 'type', | ||||||
|  |     'macro': 'macro', | ||||||
|  |     # 'const': 'const',  # all undocumented | ||||||
|  |     'typedef': 'type', | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| @generator("doc_list", 'Doc/data/stable_abi.dat') | @generator("doc_list", 'Doc/data/stable_abi.dat') | ||||||
| def gen_doc_annotations(manifest, args, outfile): | def gen_doc_annotations(manifest, args, outfile): | ||||||
|     """Generate/check the stable ABI list for documentation annotations""" |     """Generate/check the stable ABI list for documentation annotations""" | ||||||
|     write = partial(print, file=outfile) |     writer = csv.DictWriter( | ||||||
|     write("# Generated by Tools/scripts/stable_abi.py") |         outfile, ['role', 'name', 'added', 'ifdef_note'], lineterminator='\n') | ||||||
|     write() |     writer.writeheader() | ||||||
|     for item in manifest.select(ABIItem.KINDS, include_abi_only=False): |     for item in manifest.select(REST_ROLES.keys(), include_abi_only=False): | ||||||
|         write(item.name) |         if item.ifdef: | ||||||
| 
 |             ifdef_note = IFDEF_DOC_NOTES[item.ifdef] | ||||||
|  |         else: | ||||||
|  |             ifdef_note = None | ||||||
|  |         writer.writerow({ | ||||||
|  |             'role': REST_ROLES[item.kind], | ||||||
|  |             'name': item.name, | ||||||
|  |             'added': item.added, | ||||||
|  |             'ifdef_note': ifdef_note}) | ||||||
| 
 | 
 | ||||||
| def generate_or_check(manifest, args, path, func): | def generate_or_check(manifest, args, path, func): | ||||||
|     """Generate/check a file with a single generator |     """Generate/check a file with a single generator | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Petr Viktorin
						Petr Viktorin