2024-09-13 19:10:18 +02:00
|
|
|
---
|
|
|
|
Doc/conf.py | 4 +-
|
|
|
|
Doc/tools/check-warnings.py | 3 +
|
|
|
|
Doc/tools/extensions/audit_events.py | 54 ++++++++++++++++----------------
|
|
|
|
Doc/tools/extensions/c_annotations.py | 33 ++++++++-----------
|
|
|
|
Doc/tools/extensions/glossary_search.py | 10 +----
|
|
|
|
Doc/tools/extensions/patchlevel.py | 9 ++---
|
|
|
|
6 files changed, 55 insertions(+), 58 deletions(-)
|
|
|
|
|
|
|
|
--- a/Doc/conf.py
|
|
|
|
+++ b/Doc/conf.py
|
|
|
|
@@ -76,7 +76,7 @@ today_fmt = '%B %d, %Y'
|
|
|
|
highlight_language = 'python3'
|
|
|
|
|
|
|
|
# Minimum version of sphinx required
|
|
|
|
-needs_sphinx = '6.2.1'
|
|
|
|
+needs_sphinx = '4.2.0'
|
|
|
|
|
|
|
|
# Create table of contents entries for domain objects (e.g. functions, classes,
|
|
|
|
# attributes, etc.). Default is True.
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -329,7 +329,7 @@ html_short_title = f'{release} Documenta
|
2024-09-13 19:10:18 +02:00
|
|
|
# (See .readthedocs.yml and https://docs.readthedocs.io/en/stable/reference/environment-variables.html)
|
|
|
|
is_deployment_preview = os.getenv("READTHEDOCS_VERSION_TYPE") == "external"
|
|
|
|
repository_url = os.getenv("READTHEDOCS_GIT_CLONE_URL", "")
|
|
|
|
-repository_url = repository_url.removesuffix(".git")
|
|
|
|
+repository_url = repository_url[:-len(".git")]
|
|
|
|
html_context = {
|
|
|
|
"is_deployment_preview": is_deployment_preview,
|
|
|
|
"repository_url": repository_url or None,
|
|
|
|
--- a/Doc/tools/check-warnings.py
|
|
|
|
+++ b/Doc/tools/check-warnings.py
|
|
|
|
@@ -228,7 +228,8 @@ def fail_if_regression(
|
|
|
|
print(filename)
|
|
|
|
for warning in warnings:
|
|
|
|
if filename in warning:
|
|
|
|
- if match := WARNING_PATTERN.fullmatch(warning):
|
|
|
|
+ match = WARNING_PATTERN.fullmatch(warning)
|
|
|
|
+ if match:
|
|
|
|
print(" {line}: {msg}".format_map(match))
|
|
|
|
return -1
|
|
|
|
return 0
|
|
|
|
--- a/Doc/tools/extensions/audit_events.py
|
|
|
|
+++ b/Doc/tools/extensions/audit_events.py
|
|
|
|
@@ -1,9 +1,6 @@
|
|
|
|
"""Support for documenting audit events."""
|
|
|
|
|
|
|
|
-from __future__ import annotations
|
|
|
|
-
|
|
|
|
import re
|
|
|
|
-from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
from docutils import nodes
|
|
|
|
from sphinx.errors import NoUri
|
|
|
|
@@ -12,12 +9,11 @@ from sphinx.transforms.post_transforms i
|
|
|
|
from sphinx.util import logging
|
|
|
|
from sphinx.util.docutils import SphinxDirective
|
|
|
|
|
|
|
|
-if TYPE_CHECKING:
|
|
|
|
- from collections.abc import Iterator
|
|
|
|
+from typing import Any, List, Tuple
|
|
|
|
|
|
|
|
- from sphinx.application import Sphinx
|
|
|
|
- from sphinx.builders import Builder
|
|
|
|
- from sphinx.environment import BuildEnvironment
|
|
|
|
+from sphinx.application import Sphinx
|
|
|
|
+from sphinx.builders import Builder
|
|
|
|
+from sphinx.environment import BuildEnvironment
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
@@ -32,16 +28,16 @@ _SYNONYMS = [
|
|
|
|
|
|
|
|
class AuditEvents:
|
|
|
|
def __init__(self) -> None:
|
|
|
|
- self.events: dict[str, list[str]] = {}
|
|
|
|
- self.sources: dict[str, list[tuple[str, str]]] = {}
|
|
|
|
+ self.events: dict[str, List[str]] = {}
|
|
|
|
+ self.sources: dict[str, List[Tuple[str, str]]] = {}
|
|
|
|
|
|
|
|
- def __iter__(self) -> Iterator[tuple[str, list[str], tuple[str, str]]]:
|
|
|
|
+ def __iter__(self) -> Any:
|
|
|
|
for name, args in self.events.items():
|
|
|
|
for source in self.sources[name]:
|
|
|
|
yield name, args, source
|
|
|
|
|
|
|
|
def add_event(
|
|
|
|
- self, name, args: list[str], source: tuple[str, str]
|
|
|
|
+ self, name, args: List[str], source: Tuple[str, str]
|
|
|
|
) -> None:
|
|
|
|
if name in self.events:
|
|
|
|
self._check_args_match(name, args)
|
|
|
|
@@ -49,7 +45,7 @@ class AuditEvents:
|
|
|
|
self.events[name] = args
|
|
|
|
self.sources.setdefault(name, []).append(source)
|
|
|
|
|
|
|
|
- def _check_args_match(self, name: str, args: list[str]) -> None:
|
|
|
|
+ def _check_args_match(self, name: str, args: List[str]) -> None:
|
|
|
|
current_args = self.events[name]
|
|
|
|
msg = (
|
|
|
|
f"Mismatched arguments for audit-event {name}: "
|
|
|
|
@@ -60,7 +56,7 @@ class AuditEvents:
|
|
|
|
if len(current_args) != len(args):
|
|
|
|
logger.warning(msg)
|
|
|
|
return
|
|
|
|
- for a1, a2 in zip(current_args, args, strict=False):
|
|
|
|
+ for a1, a2 in zip(current_args, args):
|
|
|
|
if a1 == a2:
|
|
|
|
continue
|
|
|
|
if any(a1 in s and a2 in s for s in _SYNONYMS):
|
|
|
|
@@ -73,7 +69,7 @@ class AuditEvents:
|
|
|
|
name_clean = re.sub(r"\W", "_", name)
|
|
|
|
return f"audit_event_{name_clean}_{source_count}"
|
|
|
|
|
|
|
|
- def rows(self) -> Iterator[tuple[str, list[str], list[tuple[str, str]]]]:
|
|
|
|
+ def rows(self) -> Any:
|
|
|
|
for name in sorted(self.events.keys()):
|
|
|
|
yield name, self.events[name], self.sources[name]
|
|
|
|
|
|
|
|
@@ -97,7 +93,7 @@ def audit_events_purge(
|
|
|
|
def audit_events_merge(
|
|
|
|
app: Sphinx,
|
|
|
|
env: BuildEnvironment,
|
|
|
|
- docnames: list[str],
|
|
|
|
+ docnames: List[str],
|
|
|
|
other: BuildEnvironment,
|
|
|
|
) -> None:
|
|
|
|
"""In Sphinx parallel builds, this merges audit_events from subprocesses."""
|
|
|
|
@@ -126,14 +122,16 @@ class AuditEvent(SphinxDirective):
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
- def run(self) -> list[nodes.paragraph]:
|
|
|
|
+ def run(self) -> List[nodes.paragraph]:
|
|
|
|
+ def _no_walrus_op(args):
|
|
|
|
+ for arg in args.strip("'\"").split(","):
|
|
|
|
+ aarg = arg.strip()
|
|
|
|
+ if aarg:
|
|
|
|
+ yield aarg
|
|
|
|
+
|
|
|
|
name = self.arguments[0]
|
|
|
|
if len(self.arguments) >= 2 and self.arguments[1]:
|
|
|
|
- args = [
|
|
|
|
- arg
|
|
|
|
- for argument in self.arguments[1].strip("'\"").split(",")
|
|
|
|
- if (arg := argument.strip())
|
|
|
|
- ]
|
|
|
|
+ args = list(_no_walrus_op(self.arguments[1]))
|
|
|
|
else:
|
|
|
|
args = []
|
|
|
|
ids = []
|
|
|
|
@@ -169,7 +167,7 @@ class audit_event_list(nodes.General, no
|
|
|
|
|
|
|
|
|
|
|
|
class AuditEventListDirective(SphinxDirective):
|
|
|
|
- def run(self) -> list[audit_event_list]:
|
|
|
|
+ def run(self) -> List[audit_event_list]:
|
|
|
|
return [audit_event_list()]
|
|
|
|
|
|
|
|
|
|
|
|
@@ -181,7 +179,11 @@ class AuditEventListTransform(SphinxPost
|
|
|
|
return
|
|
|
|
|
|
|
|
table = self._make_table(self.app.builder, self.env.docname)
|
|
|
|
- for node in self.document.findall(audit_event_list):
|
|
|
|
+ try:
|
|
|
|
+ findall = self.document.findall
|
|
|
|
+ except AttributeError:
|
|
|
|
+ findall = self.document.traverse
|
|
|
|
+ for node in findall(audit_event_list):
|
|
|
|
node.replace_self(table)
|
|
|
|
|
|
|
|
def _make_table(self, builder: Builder, docname: str) -> nodes.table:
|
|
|
|
@@ -217,8 +219,8 @@ class AuditEventListTransform(SphinxPost
|
|
|
|
builder: Builder,
|
|
|
|
docname: str,
|
|
|
|
name: str,
|
|
|
|
- args: list[str],
|
|
|
|
- sources: list[tuple[str, str]],
|
|
|
|
+ args: List[str],
|
|
|
|
+ sources: List[Tuple[str, str]],
|
|
|
|
) -> nodes.row:
|
|
|
|
row = nodes.row()
|
|
|
|
name_node = nodes.paragraph("", nodes.Text(name))
|
|
|
|
--- a/Doc/tools/extensions/c_annotations.py
|
|
|
|
+++ b/Doc/tools/extensions/c_annotations.py
|
|
|
|
@@ -9,12 +9,10 @@ Configuration:
|
|
|
|
* Set ``stable_abi_file`` to the path to stable ABI list.
|
|
|
|
"""
|
|
|
|
|
|
|
|
-from __future__ import annotations
|
|
|
|
-
|
|
|
|
import csv
|
|
|
|
import dataclasses
|
|
|
|
from pathlib import Path
|
|
|
|
-from typing import TYPE_CHECKING
|
|
|
|
+from typing import Any, Dict, List, TYPE_CHECKING, Union
|
|
|
|
|
|
|
|
import sphinx
|
|
|
|
from docutils import nodes
|
|
|
|
@@ -23,9 +21,7 @@ from sphinx import addnodes
|
|
|
|
from sphinx.locale import _ as sphinx_gettext
|
|
|
|
from sphinx.util.docutils import SphinxDirective
|
|
|
|
|
|
|
|
-if TYPE_CHECKING:
|
|
|
|
- from sphinx.application import Sphinx
|
|
|
|
- from sphinx.util.typing import ExtensionMetadata
|
|
|
|
+from sphinx.application import Sphinx
|
|
|
|
|
|
|
|
ROLE_TO_OBJECT_TYPE = {
|
|
|
|
"func": "function",
|
|
|
|
@@ -36,20 +32,20 @@ ROLE_TO_OBJECT_TYPE = {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-@dataclasses.dataclass(slots=True)
|
|
|
|
+@dataclasses.dataclass()
|
|
|
|
class RefCountEntry:
|
|
|
|
# Name of the function.
|
|
|
|
name: str
|
|
|
|
# List of (argument name, type, refcount effect) tuples.
|
|
|
|
# (Currently not used. If it was, a dataclass might work better.)
|
|
|
|
- args: list = dataclasses.field(default_factory=list)
|
|
|
|
+ args: List = dataclasses.field(default_factory=list)
|
|
|
|
# Return type of the function.
|
|
|
|
result_type: str = ""
|
|
|
|
# Reference count effect for the return value.
|
|
|
|
- result_refs: int | None = None
|
|
|
|
+ result_refs: Union[int, None] = None
|
|
|
|
|
|
|
|
|
|
|
|
-@dataclasses.dataclass(frozen=True, slots=True)
|
|
|
|
+@dataclasses.dataclass(frozen=True)
|
|
|
|
class StableABIEntry:
|
|
|
|
# Role of the object.
|
|
|
|
# Source: Each [item_kind] in stable_abi.toml is mapped to a C Domain role.
|
|
|
|
@@ -68,7 +64,7 @@ class StableABIEntry:
|
|
|
|
struct_abi_kind: str
|
|
|
|
|
|
|
|
|
|
|
|
-def read_refcount_data(refcount_filename: Path) -> dict[str, RefCountEntry]:
|
|
|
|
+def read_refcount_data(refcount_filename: Path) -> Dict[str, RefCountEntry]:
|
|
|
|
refcount_data = {}
|
|
|
|
refcounts = refcount_filename.read_text(encoding="utf8")
|
|
|
|
for line in refcounts.splitlines():
|
|
|
|
@@ -104,7 +100,7 @@ def read_refcount_data(refcount_filename
|
|
|
|
return refcount_data
|
|
|
|
|
|
|
|
|
|
|
|
-def read_stable_abi_data(stable_abi_file: Path) -> dict[str, StableABIEntry]:
|
|
|
|
+def read_stable_abi_data(stable_abi_file: Path) -> Dict[str, StableABIEntry]:
|
|
|
|
stable_abi_data = {}
|
|
|
|
with open(stable_abi_file, encoding="utf8") as fp:
|
|
|
|
for record in csv.DictReader(fp):
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -132,7 +128,8 @@ def add_annotations(app: Sphinx, doctree
|
2024-09-13 19:10:18 +02:00
|
|
|
objtype = par["objtype"]
|
|
|
|
|
|
|
|
# Stable ABI annotation.
|
|
|
|
- if record := stable_abi_data.get(name):
|
|
|
|
+ record = stable_abi_data.get(name)
|
|
|
|
+ if record:
|
|
|
|
if ROLE_TO_OBJECT_TYPE[record.role] != objtype:
|
|
|
|
msg = (
|
|
|
|
f"Object type mismatch in limited API annotation for {name}: "
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -239,7 +236,7 @@ def _unstable_api_annotation() -> nodes.
|
2024-09-13 19:10:18 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
-def _return_value_annotation(result_refs: int | None) -> nodes.emphasis:
|
|
|
|
+def _return_value_annotation(result_refs: Union[int, None]) -> nodes.emphasis:
|
|
|
|
classes = ["refcount"]
|
|
|
|
if result_refs is None:
|
|
|
|
rc = sphinx_gettext("Return value: Always NULL.")
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -259,7 +256,7 @@ class LimitedAPIList(SphinxDirective):
|
2024-09-13 19:10:18 +02:00
|
|
|
optional_arguments = 0
|
|
|
|
final_argument_whitespace = True
|
|
|
|
|
|
|
|
- def run(self) -> list[nodes.Node]:
|
|
|
|
+ def run(self) -> List[nodes.Node]:
|
|
|
|
state = self.env.domaindata["c_annotations"]
|
|
|
|
content = [
|
|
|
|
f"* :c:{record.role}:`{record.name}`"
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -282,7 +279,7 @@ def init_annotations(app: Sphinx) -> Non
|
2024-09-13 19:10:18 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
|
+def setup(app: Sphinx) -> Any:
|
|
|
|
app.add_config_value("refcount_file", "", "env", types={str})
|
|
|
|
app.add_config_value("stable_abi_file", "", "env", types={str})
|
|
|
|
app.add_directive("limited-api-list", LimitedAPIList)
|
2024-10-01 17:35:43 +02:00
|
|
|
@@ -294,10 +291,10 @@ def setup(app: Sphinx) -> ExtensionMetad
|
2024-09-13 19:10:18 +02:00
|
|
|
from sphinx.domains.c import CObject
|
|
|
|
|
|
|
|
# monkey-patch C object...
|
|
|
|
- CObject.option_spec |= {
|
|
|
|
+ CObject.option_spec.update({
|
|
|
|
"no-index-entry": directives.flag,
|
|
|
|
"no-contents-entry": directives.flag,
|
|
|
|
- }
|
|
|
|
+ })
|
|
|
|
|
|
|
|
return {
|
|
|
|
"version": "1.0",
|
|
|
|
--- a/Doc/tools/extensions/glossary_search.py
|
|
|
|
+++ b/Doc/tools/extensions/glossary_search.py
|
|
|
|
@@ -1,18 +1,14 @@
|
|
|
|
"""Feature search results for glossary items prominently."""
|
|
|
|
|
|
|
|
-from __future__ import annotations
|
|
|
|
-
|
|
|
|
import json
|
|
|
|
from pathlib import Path
|
|
|
|
-from typing import TYPE_CHECKING
|
|
|
|
+from typing import Any, TYPE_CHECKING
|
|
|
|
|
|
|
|
from docutils import nodes
|
|
|
|
from sphinx.addnodes import glossary
|
|
|
|
from sphinx.util import logging
|
|
|
|
|
|
|
|
-if TYPE_CHECKING:
|
|
|
|
- from sphinx.application import Sphinx
|
|
|
|
- from sphinx.util.typing import ExtensionMetadata
|
|
|
|
+from sphinx.application import Sphinx
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
@@ -60,7 +56,7 @@ def write_glossary_json(app: Sphinx, _ex
|
|
|
|
dest.write_text(json.dumps(app.env.glossary_terms), encoding='utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
|
+def setup(app: Sphinx) -> Any:
|
|
|
|
app.connect('doctree-resolved', process_glossary_nodes)
|
|
|
|
app.connect('build-finished', write_glossary_json)
|
|
|
|
|
|
|
|
--- a/Doc/tools/extensions/patchlevel.py
|
|
|
|
+++ b/Doc/tools/extensions/patchlevel.py
|
|
|
|
@@ -3,7 +3,7 @@
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
from pathlib import Path
|
|
|
|
-from typing import Literal, NamedTuple
|
|
|
|
+from typing import NamedTuple, Tuple
|
|
|
|
|
|
|
|
CPYTHON_ROOT = Path(
|
|
|
|
__file__, # cpython/Doc/tools/extensions/patchlevel.py
|
|
|
|
@@ -26,7 +26,7 @@ class version_info(NamedTuple): # noqa:
|
|
|
|
major: int #: Major release number
|
|
|
|
minor: int #: Minor release number
|
|
|
|
micro: int #: Patch release number
|
|
|
|
- releaselevel: Literal["alpha", "beta", "candidate", "final"]
|
|
|
|
+ releaselevel: str
|
|
|
|
serial: int #: Serial release number
|
|
|
|
|
|
|
|
|
|
|
|
@@ -37,7 +37,8 @@ def get_header_version_info() -> version
|
|
|
|
defines = {}
|
|
|
|
patchlevel_h = PATCHLEVEL_H.read_text(encoding="utf-8")
|
|
|
|
for line in patchlevel_h.splitlines():
|
|
|
|
- if (m := pat.match(line)) is not None:
|
|
|
|
+ m = pat.match(line)
|
|
|
|
+ if m is not None:
|
|
|
|
name, value = m.groups()
|
|
|
|
defines[name] = value
|
|
|
|
|
|
|
|
@@ -50,7 +51,7 @@ def get_header_version_info() -> version
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
-def format_version_info(info: version_info) -> tuple[str, str]:
|
|
|
|
+def format_version_info(info: version_info) -> Tuple[str, str]:
|
|
|
|
version = f"{info.major}.{info.minor}"
|
|
|
|
release = f"{info.major}.{info.minor}.{info.micro}"
|
|
|
|
if info.releaselevel != "final":
|