code: def add_annotations(self, app, doctree):
      for node in doctree.findall(addnodes.desc_content):
          par = node.parent
          if par["domain"] != "c":
              continue
          if not par[0].has_key("ids") or not par[0]["ids"]:
              continue
          name = par[0]["ids"][0]
          if name.startswith("c."):
              name = name[2:]
  
          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.
          # For structs, there's some more info in the message:
          #   Part of the [Limited API](link) (as an opaque struct).
          #   Part of the [Stable ABI](link) (including all members).
          #   Part of the [Limited API](link) (Only some members are part
          #       of the stable ABI.).
          # ... all of which can have "since version X.Y" appended.
          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 = sphinx_gettext("Part of the")
              message = message.center(len(message) + 2)
              emph_node = nodes.emphasis(
                  message, message, classes=["stableabi"]
              )
              ref_node = addnodes.pending_xref(
                  "Stable ABI",
                  refdomain="std",
                  reftarget="stable",
                  reftype="ref",
                  refexplicit="False",
              )
              struct_abi_kind = record["struct_abi_kind"]
              if struct_abi_kind in {"opaque", "members"}:
                  ref_node += nodes.Text(sphinx_gettext("Limited API"))
              else:
                  ref_node += nodes.Text(sphinx_gettext("Stable ABI"))
              emph_node += ref_node
              if struct_abi_kind == "opaque":
                  emph_node += nodes.Text(
                      " " + sphinx_gettext("(as an opaque struct)")
                  )
              elif struct_abi_kind == "full-abi":
                  emph_node += nodes.Text(
                      " " + sphinx_gettext("(including all members)")
                  )
              if record["ifdef_note"]:
                  emph_node += nodes.Text(" " + record["ifdef_note"])
              if stable_added == "3.2":
                  # Stable ABI was introduced in 3.2.
                  pass
              else:
                  emph_node += nodes.Text(
                      " "
                      + sphinx_gettext("since version %s")
                      % stable_added
                  )
              emph_node += nodes.Text(".")
              if struct_abi_kind == "members":
                  emph_node += nodes.Text(
                      " "
                      + sphinx_gettext(
                          "(Only some members are part of the stable ABI.)"
                      )
                  )
              node.insert(0, emph_node)
  
          # Unstable API annotation.
          if name.startswith("PyUnstable"):
              warn_node = nodes.admonition(
                  classes=["unstable-c-api", "warning"]
              )
              message = sphinx_gettext("This is") + " "
              emph_node = nodes.emphasis(message, message)
              ref_node = addnodes.pending_xref(
                  "Unstable API",
                  refdomain="std",
                  reftarget="unstable-c-api",
                  reftype="ref",
                  refexplicit="False",
              )
              ref_node += nodes.Text(sphinx_gettext("Unstable API"))
              emph_node += ref_node
              emph_node += nodes.Text(
                  sphinx_gettext(
                      ". It may change without warning in minor releases."
                  )
              )
              warn_node += emph_node
              node.insert(0, warn_node)
  
          # Return value annotation
          if objtype != "function":
              continue
          entry = self.refcount_data.get(name)
          if not entry:
              continue
          elif not entry.result_type.endswith("Object*"):
              continue
          if entry.result_refs is None:
              rc = sphinx_gettext("Return value: Always NULL.")
          elif entry.result_refs:
              rc = sphinx_gettext("Return value: New reference.")
          else:
              rc = sphinx_gettext("Return value: Borrowed reference.")
          node.insert(0, nodes.emphasis(rc, rc, classes=["refcount"]))
  
file_path: cpython/Doc/tools/extensions/c_annotations.py
imports: from os import path
  import docutils
  from docutils import nodes
  from docutils.parsers.rst import directives
  from docutils.parsers.rst import Directive
  from docutils.statemachine import StringList
  from sphinx.locale import _
  import csv
  from sphinx import addnodes
  from sphinx.domains.c import CObject
label_desc: Use image node_id 2 for calling the Annotations obj's underlying member method code with example usage: obj.add_annotations(app, doctree) without return types