From 6df1740938bc2156ae28ad5989797598c41b162f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:50:59 +0200 Subject: [PATCH 1/7] add an extension for indicating library requirements --- Doc/conf.py | 1 + Doc/tools/extensions/__init__.py | 0 Doc/tools/extensions/availability.py | 95 ++++++++----------------- Doc/tools/extensions/requirements.py | 64 +++++++++++++++++ Doc/tools/extensions/support.py | 100 +++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 68 deletions(-) create mode 100644 Doc/tools/extensions/__init__.py create mode 100644 Doc/tools/extensions/requirements.py create mode 100644 Doc/tools/extensions/support.py diff --git a/Doc/conf.py b/Doc/conf.py index bdc3ad13617948..532b8a297d935f 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -35,6 +35,7 @@ 'misc_news', 'pydoc_topics', 'pyspecific', + 'requirements', 'sphinx.ext.coverage', 'sphinx.ext.doctest', 'sphinx.ext.extlinks', diff --git a/Doc/tools/extensions/__init__.py b/Doc/tools/extensions/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py index 1a2c7b02b44439..0cd966732d5c55 100644 --- a/Doc/tools/extensions/availability.py +++ b/Doc/tools/extensions/availability.py @@ -4,11 +4,8 @@ from typing import TYPE_CHECKING -from docutils import nodes -from sphinx import addnodes -from sphinx.locale import _ as sphinx_gettext from sphinx.util import logging -from sphinx.util.docutils import SphinxDirective +from support import SmartItemList if TYPE_CHECKING: from sphinx.application import Sphinx @@ -49,71 +46,33 @@ KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING -class Availability(SphinxDirective): - has_content = True - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = True - - def run(self) -> list[nodes.container]: - title = sphinx_gettext("Availability") - refnode = addnodes.pending_xref( - title, - nodes.inline(title, title, classes=["xref", "std", "std-ref"]), - refdoc=self.env.docname, - refdomain="std", - refexplicit=True, - reftarget="availability", - reftype="ref", - refwarn=True, +class Availability(SmartItemList): + """Parse platform information from arguments + + Arguments is a comma-separated string of platforms. A platform may + be prefixed with "not " to indicate that a feature is not available. + + Example:: + + .. availability:: Windows, Linux >= 4.2, not WASI + + Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not + parsed into separate tokens. + """ + + title = "Availability" + reftarget = "availability" + classes = ["availability"] + + def check_information(self, platforms: dict[str, str | bool], /) -> None: + unknown = platforms.keys() - KNOWN_PLATFORMS + self._check_information( + logger, + f"{__file__}:KNOWN_PLATFORMS", + unknown, + ("platform", "platforms"), + len(platforms), ) - sep = nodes.Text(": ") - parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno) - pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs) - self.set_source_info(pnode) - cnode = nodes.container("", pnode, classes=["availability"]) - self.set_source_info(cnode) - if self.content: - self.state.nested_parse(self.content, self.content_offset, cnode) - self.parse_platforms() - - return [cnode] - - def parse_platforms(self) -> dict[str, str | bool]: - """Parse platform information from arguments - - Arguments is a comma-separated string of platforms. A platform may - be prefixed with "not " to indicate that a feature is not available. - - Example:: - - .. availability:: Windows, Linux >= 4.2, not WASI - - Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not - parsed into separate tokens. - """ - platforms = {} - for arg in self.arguments[0].rstrip(".").split(","): - arg = arg.strip() - platform, _, version = arg.partition(" >= ") - if platform.startswith("not "): - version = False - platform = platform.removeprefix("not ") - elif not version: - version = True - platforms[platform] = version - - if unknown := set(platforms).difference(KNOWN_PLATFORMS): - logger.warning( - "Unknown platform%s or syntax '%s' in '.. availability:: %s', " - "see %s:KNOWN_PLATFORMS for a set of known platforms.", - "s" if len(platforms) != 1 else "", - " ".join(sorted(unknown)), - self.arguments[0], - __file__, - ) - - return platforms def setup(app: Sphinx) -> ExtensionMetadata: diff --git a/Doc/tools/extensions/requirements.py b/Doc/tools/extensions/requirements.py new file mode 100644 index 00000000000000..810b3314989485 --- /dev/null +++ b/Doc/tools/extensions/requirements.py @@ -0,0 +1,64 @@ +"""Support for documenting system library requirements.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from sphinx.util import logging +from support import SmartItemList + +if TYPE_CHECKING: + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata + + +logger = logging.getLogger("requirements") + +# Known non-vendored dependencies. +KNOWN_REQUIREMENTS = frozenset({ + # libssl implementations + "OpenSSL", + "AWS-LC", + "BoringSSL", + "LibreSSL", +}) + + +class Requirements(SmartItemList): + """Parse dependencies information from arguments. + + Arguments is a comma-separated string of dependencies. A dependency may + be prefixed with "not " to indicate that a feature is not available if + it was used. + + Example:: + + .. availability:: OpenSSL >= 3.5, not BoringSSL + + Arguments like "OpenSSL >= 3.5 with FIPS mode on" are currently not + parsed into separate tokens. + """ + + title = "Requirements" + reftarget = "requirements-notes" + classes = ["requirements"] + + def check_information(self, requirements, /): + unknown = requirements.keys() - KNOWN_REQUIREMENTS + self._check_information( + logger, + f"{__file__}:KNOWN_REQUIREMENTS", + unknown, + ("requirement", "requirements"), + len(requirements), + ) + + +def setup(app: Sphinx) -> ExtensionMetadata: + app.add_directive("requirements", Requirements) + + return { + "version": "1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/Doc/tools/extensions/support.py b/Doc/tools/extensions/support.py new file mode 100644 index 00000000000000..1078ca494fbf92 --- /dev/null +++ b/Doc/tools/extensions/support.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from docutils import nodes +from sphinx import addnodes +from sphinx.locale import _ as sphinx_gettext +from sphinx.util.docutils import SphinxDirective + +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + from logging import Logger + from typing import ClassVar + + +class SmartItemList(SphinxDirective): + title: ClassVar[str] + reftarget: ClassVar[str] + classes: ClassVar[Sequence[str]] + + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + + def run(self) -> list[nodes.container]: + title = sphinx_gettext(self.title) + refnode = addnodes.pending_xref( + title, + nodes.inline(title, title, classes=["xref", "std", "std-ref"]), + refdoc=self.env.docname, + refdomain="std", + refexplicit=True, + reftarget=self.reftarget, + reftype="ref", + refwarn=True, + ) + sep = nodes.Text(": ") + parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno) + pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs) + self.set_source_info(pnode) + cnode = nodes.container("", pnode, classes=self.classes) + self.set_source_info(cnode) + if self.content: + self.state.nested_parse(self.content, self.content_offset, cnode) + + items = self.parse_information() + self.check_information(items) + + return [cnode] + + def parse_information(self) -> dict[str, str | bool]: + """Parse information from arguments. + + Arguments is a comma-separated string of versioned named items. + Each item may be prefixed with "not " to indicate that a feature + is not available. + + Example:: + + .. :: , >= , not + + Arguments like " >= major.minor with >= x.y.z" are + currently not parsed into separate tokens. + """ + items = {} + for arg in self.arguments[0].rstrip(".").split(","): + arg = arg.strip() + name, _, version = arg.partition(" >= ") + if name.startswith("not "): + version = False + name = name.removeprefix("not ") + elif not version: + version = True + items[name] = version + return items + + def check_information(self, items: dict[str, str | bool], /) -> None: + raise NotImplementedError + + def _check_information( + self, + logger: Logger, + seealso: str, + unknown: Iterable[str], + parsed_items_descr: tuple[str, str], + parsed_items_count: int, + /, + ): + if unknown := " ".join(sorted(unknown)): + logger.warning( + "Unknown %s or syntax '%s' in '.. %s:: %s', " + "see %s for a set of known %s.", + parsed_items_descr[int(parsed_items_count > 1)], + unknown, + self.name, + self.arguments[0], + seealso, + parsed_items_descr[1], + ) From 5f351d20957dc879f6a4f4723be1addac6fbdfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:41:58 +0200 Subject: [PATCH 2/7] indicate OpenSSL requirements for new functions --- Doc/library/intro.rst | 12 ++++++++++++ Doc/library/ssl.rst | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Doc/library/intro.rst b/Doc/library/intro.rst index 8f76044be488cd..aff8c9af46449b 100644 --- a/Doc/library/intro.rst +++ b/Doc/library/intro.rst @@ -48,6 +48,18 @@ this material. Let the show begin! +.. _requirements-notes: + +Notes on requirements +===================== + +* A "Requirements: OpenSSL >= 3.5" note means that this function requires + that Python has been built with OpenSSL 3.5 or later. + +* A "Requirements: not AWS-LC" note means that this function is not available + if Python has been built with AWS-LC instead of OpenSSL. + + .. _availability: Notes on availability diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 0f2c2b89295cdf..765073750f113d 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -232,6 +232,8 @@ Signature algorithms :meth:`SSLContext.set_client_sigalgs` and :meth:`SSLContext.set_server_sigalgs` methods. + .. requirements:: OpenSSL >= 3.4 + .. versionadded:: next @@ -1318,6 +1320,8 @@ SSL sockets also have the following additional methods and attributes: Return the group used for doing key agreement on this connection. If no connection has been established, returns ``None``. + .. requirements:: OpenSSL >= 3.2 + .. versionadded:: next .. method:: SSLSocket.client_sigalg() @@ -1326,6 +1330,8 @@ SSL sockets also have the following additional methods and attributes: authentication on this connection, or ``None`` if no connection has been established or client authentication didn't occur. + .. requirements:: OpenSSL >= 3.5 + .. versionadded:: next .. method:: SSLSocket.server_sigalg() @@ -1334,6 +1340,8 @@ SSL sockets also have the following additional methods and attributes: handshake on this connection, or ``None`` if no connection has been established or the cipher suite has no signature. + .. requirements:: OpenSSL >= 3.5 + .. versionadded:: next .. method:: SSLSocket.compression() @@ -1710,6 +1718,8 @@ to speed up repeated connections from the same clients. :const:`True` this method will also return any associated aliases such as the ECDH curve names supported in older versions of OpenSSL. + .. requirements:: OpenSSL >= 3.5 + .. versionadded:: next .. method:: SSLContext.set_default_verify_paths() @@ -1777,6 +1787,8 @@ to speed up repeated connections from the same clients. sockets will return the signature algorithm used for performing certificate-based client authentication on that connection. + .. requirements:: not AWS-LC + .. versionadded:: next .. method:: SSLContext.set_server_sigalgs(sigalgs, /) From dca32b571905ce799c83ba53b11da94987f65e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:54:11 +0200 Subject: [PATCH 3/7] Revert "add an extension for indicating library requirements" This reverts commit 6df1740938bc2156ae28ad5989797598c41b162f. --- Doc/conf.py | 1 - Doc/tools/extensions/__init__.py | 0 Doc/tools/extensions/availability.py | 95 +++++++++++++++++-------- Doc/tools/extensions/requirements.py | 64 ----------------- Doc/tools/extensions/support.py | 100 --------------------------- 5 files changed, 68 insertions(+), 192 deletions(-) delete mode 100644 Doc/tools/extensions/__init__.py delete mode 100644 Doc/tools/extensions/requirements.py delete mode 100644 Doc/tools/extensions/support.py diff --git a/Doc/conf.py b/Doc/conf.py index 532b8a297d935f..bdc3ad13617948 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -35,7 +35,6 @@ 'misc_news', 'pydoc_topics', 'pyspecific', - 'requirements', 'sphinx.ext.coverage', 'sphinx.ext.doctest', 'sphinx.ext.extlinks', diff --git a/Doc/tools/extensions/__init__.py b/Doc/tools/extensions/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py index 0cd966732d5c55..1a2c7b02b44439 100644 --- a/Doc/tools/extensions/availability.py +++ b/Doc/tools/extensions/availability.py @@ -4,8 +4,11 @@ from typing import TYPE_CHECKING +from docutils import nodes +from sphinx import addnodes +from sphinx.locale import _ as sphinx_gettext from sphinx.util import logging -from support import SmartItemList +from sphinx.util.docutils import SphinxDirective if TYPE_CHECKING: from sphinx.application import Sphinx @@ -46,33 +49,71 @@ KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING -class Availability(SmartItemList): - """Parse platform information from arguments - - Arguments is a comma-separated string of platforms. A platform may - be prefixed with "not " to indicate that a feature is not available. - - Example:: - - .. availability:: Windows, Linux >= 4.2, not WASI - - Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not - parsed into separate tokens. - """ - - title = "Availability" - reftarget = "availability" - classes = ["availability"] - - def check_information(self, platforms: dict[str, str | bool], /) -> None: - unknown = platforms.keys() - KNOWN_PLATFORMS - self._check_information( - logger, - f"{__file__}:KNOWN_PLATFORMS", - unknown, - ("platform", "platforms"), - len(platforms), +class Availability(SphinxDirective): + has_content = True + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + + def run(self) -> list[nodes.container]: + title = sphinx_gettext("Availability") + refnode = addnodes.pending_xref( + title, + nodes.inline(title, title, classes=["xref", "std", "std-ref"]), + refdoc=self.env.docname, + refdomain="std", + refexplicit=True, + reftarget="availability", + reftype="ref", + refwarn=True, ) + sep = nodes.Text(": ") + parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno) + pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs) + self.set_source_info(pnode) + cnode = nodes.container("", pnode, classes=["availability"]) + self.set_source_info(cnode) + if self.content: + self.state.nested_parse(self.content, self.content_offset, cnode) + self.parse_platforms() + + return [cnode] + + def parse_platforms(self) -> dict[str, str | bool]: + """Parse platform information from arguments + + Arguments is a comma-separated string of platforms. A platform may + be prefixed with "not " to indicate that a feature is not available. + + Example:: + + .. availability:: Windows, Linux >= 4.2, not WASI + + Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not + parsed into separate tokens. + """ + platforms = {} + for arg in self.arguments[0].rstrip(".").split(","): + arg = arg.strip() + platform, _, version = arg.partition(" >= ") + if platform.startswith("not "): + version = False + platform = platform.removeprefix("not ") + elif not version: + version = True + platforms[platform] = version + + if unknown := set(platforms).difference(KNOWN_PLATFORMS): + logger.warning( + "Unknown platform%s or syntax '%s' in '.. availability:: %s', " + "see %s:KNOWN_PLATFORMS for a set of known platforms.", + "s" if len(platforms) != 1 else "", + " ".join(sorted(unknown)), + self.arguments[0], + __file__, + ) + + return platforms def setup(app: Sphinx) -> ExtensionMetadata: diff --git a/Doc/tools/extensions/requirements.py b/Doc/tools/extensions/requirements.py deleted file mode 100644 index 810b3314989485..00000000000000 --- a/Doc/tools/extensions/requirements.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Support for documenting system library requirements.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from sphinx.util import logging -from support import SmartItemList - -if TYPE_CHECKING: - from sphinx.application import Sphinx - from sphinx.util.typing import ExtensionMetadata - - -logger = logging.getLogger("requirements") - -# Known non-vendored dependencies. -KNOWN_REQUIREMENTS = frozenset({ - # libssl implementations - "OpenSSL", - "AWS-LC", - "BoringSSL", - "LibreSSL", -}) - - -class Requirements(SmartItemList): - """Parse dependencies information from arguments. - - Arguments is a comma-separated string of dependencies. A dependency may - be prefixed with "not " to indicate that a feature is not available if - it was used. - - Example:: - - .. availability:: OpenSSL >= 3.5, not BoringSSL - - Arguments like "OpenSSL >= 3.5 with FIPS mode on" are currently not - parsed into separate tokens. - """ - - title = "Requirements" - reftarget = "requirements-notes" - classes = ["requirements"] - - def check_information(self, requirements, /): - unknown = requirements.keys() - KNOWN_REQUIREMENTS - self._check_information( - logger, - f"{__file__}:KNOWN_REQUIREMENTS", - unknown, - ("requirement", "requirements"), - len(requirements), - ) - - -def setup(app: Sphinx) -> ExtensionMetadata: - app.add_directive("requirements", Requirements) - - return { - "version": "1.0", - "parallel_read_safe": True, - "parallel_write_safe": True, - } diff --git a/Doc/tools/extensions/support.py b/Doc/tools/extensions/support.py deleted file mode 100644 index 1078ca494fbf92..00000000000000 --- a/Doc/tools/extensions/support.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from docutils import nodes -from sphinx import addnodes -from sphinx.locale import _ as sphinx_gettext -from sphinx.util.docutils import SphinxDirective - -if TYPE_CHECKING: - from collections.abc import Iterable, Sequence - from logging import Logger - from typing import ClassVar - - -class SmartItemList(SphinxDirective): - title: ClassVar[str] - reftarget: ClassVar[str] - classes: ClassVar[Sequence[str]] - - has_content = True - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = True - - def run(self) -> list[nodes.container]: - title = sphinx_gettext(self.title) - refnode = addnodes.pending_xref( - title, - nodes.inline(title, title, classes=["xref", "std", "std-ref"]), - refdoc=self.env.docname, - refdomain="std", - refexplicit=True, - reftarget=self.reftarget, - reftype="ref", - refwarn=True, - ) - sep = nodes.Text(": ") - parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno) - pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs) - self.set_source_info(pnode) - cnode = nodes.container("", pnode, classes=self.classes) - self.set_source_info(cnode) - if self.content: - self.state.nested_parse(self.content, self.content_offset, cnode) - - items = self.parse_information() - self.check_information(items) - - return [cnode] - - def parse_information(self) -> dict[str, str | bool]: - """Parse information from arguments. - - Arguments is a comma-separated string of versioned named items. - Each item may be prefixed with "not " to indicate that a feature - is not available. - - Example:: - - .. :: , >= , not - - Arguments like " >= major.minor with >= x.y.z" are - currently not parsed into separate tokens. - """ - items = {} - for arg in self.arguments[0].rstrip(".").split(","): - arg = arg.strip() - name, _, version = arg.partition(" >= ") - if name.startswith("not "): - version = False - name = name.removeprefix("not ") - elif not version: - version = True - items[name] = version - return items - - def check_information(self, items: dict[str, str | bool], /) -> None: - raise NotImplementedError - - def _check_information( - self, - logger: Logger, - seealso: str, - unknown: Iterable[str], - parsed_items_descr: tuple[str, str], - parsed_items_count: int, - /, - ): - if unknown := " ".join(sorted(unknown)): - logger.warning( - "Unknown %s or syntax '%s' in '.. %s:: %s', " - "see %s for a set of known %s.", - parsed_items_descr[int(parsed_items_count > 1)], - unknown, - self.name, - self.arguments[0], - seealso, - parsed_items_descr[1], - ) From df4f93b5f0dd2035081b21f94ffa0d33b875a0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:11:54 +0200 Subject: [PATCH 4/7] remove un-necessary directive --- Doc/library/intro.rst | 19 +++++++------------ Doc/library/ssl.rst | 12 ++++++------ Doc/tools/extensions/availability.py | 9 +++++++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Doc/library/intro.rst b/Doc/library/intro.rst index aff8c9af46449b..78dd18415ccdd0 100644 --- a/Doc/library/intro.rst +++ b/Doc/library/intro.rst @@ -48,18 +48,6 @@ this material. Let the show begin! -.. _requirements-notes: - -Notes on requirements -===================== - -* A "Requirements: OpenSSL >= 3.5" note means that this function requires - that Python has been built with OpenSSL 3.5 or later. - -* A "Requirements: not AWS-LC" note means that this function is not available - if Python has been built with AWS-LC instead of OpenSSL. - - .. _availability: Notes on availability @@ -77,6 +65,13 @@ Notes on availability *Availability: Linux >= 3.17 with glibc >= 2.27* requires both Linux 3.17 or newer and glibc 2.27 or newer. +* "Availability" notes may also be used to indicate requirements on how Python + was built. For instance, "Availability: OpenSSL >= 3.5" note means that the + feature is available if Python has been built with OpenSSL 3.5 or later, + while "Availability: not AWS-LC" note means that the feature is not available + if Python has been built with AWS-LC instead of OpenSSL. + + .. _wasm-availability: WebAssembly platforms diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 765073750f113d..883724e1c0d3aa 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -232,7 +232,7 @@ Signature algorithms :meth:`SSLContext.set_client_sigalgs` and :meth:`SSLContext.set_server_sigalgs` methods. - .. requirements:: OpenSSL >= 3.4 + .. availability:: OpenSSL >= 3.4 .. versionadded:: next @@ -1320,7 +1320,7 @@ SSL sockets also have the following additional methods and attributes: Return the group used for doing key agreement on this connection. If no connection has been established, returns ``None``. - .. requirements:: OpenSSL >= 3.2 + .. availability:: OpenSSL >= 3.2 .. versionadded:: next @@ -1330,7 +1330,7 @@ SSL sockets also have the following additional methods and attributes: authentication on this connection, or ``None`` if no connection has been established or client authentication didn't occur. - .. requirements:: OpenSSL >= 3.5 + .. availability:: OpenSSL >= 3.5 .. versionadded:: next @@ -1340,7 +1340,7 @@ SSL sockets also have the following additional methods and attributes: handshake on this connection, or ``None`` if no connection has been established or the cipher suite has no signature. - .. requirements:: OpenSSL >= 3.5 + .. availability:: OpenSSL >= 3.5 .. versionadded:: next @@ -1718,7 +1718,7 @@ to speed up repeated connections from the same clients. :const:`True` this method will also return any associated aliases such as the ECDH curve names supported in older versions of OpenSSL. - .. requirements:: OpenSSL >= 3.5 + .. availability:: OpenSSL >= 3.5 .. versionadded:: next @@ -1787,7 +1787,7 @@ to speed up repeated connections from the same clients. sockets will return the signature algorithm used for performing certificate-based client authentication on that connection. - .. requirements:: not AWS-LC + .. availability:: not AWS-LC .. versionadded:: next diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py index 1a2c7b02b44439..74c997ff5d9674 100644 --- a/Doc/tools/extensions/availability.py +++ b/Doc/tools/extensions/availability.py @@ -42,11 +42,16 @@ "glibc", "musl", }) -_THREADING = frozenset({ +_EXTRAS = frozenset({ # POSIX platforms with pthreads "pthreads", + # SSL backends + "OpenSSL", + "AWS-LC", + "LibreSSL", + "BoringSSL", }) -KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING +KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _EXTRAS class Availability(SphinxDirective): From c1ae0f6d30aa18fb58a144e5603b2f30dfa9cd31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:56:22 +0200 Subject: [PATCH 5/7] Define SSL backends separately Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/tools/extensions/availability.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py index 74c997ff5d9674..583462c8b912e2 100644 --- a/Doc/tools/extensions/availability.py +++ b/Doc/tools/extensions/availability.py @@ -42,16 +42,17 @@ "glibc", "musl", }) -_EXTRAS = frozenset({ +_THREADING = frozenset({ # POSIX platforms with pthreads "pthreads", - # SSL backends +}) +_SSL_BACKENDS = frozenset({ "OpenSSL", "AWS-LC", "LibreSSL", "BoringSSL", }) -KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _EXTRAS +KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING | _SSL_BACKENDS class Availability(SphinxDirective): From 7c60b75252d13cc23de31300c45e61c778d71f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 30 Sep 2025 12:06:40 +0200 Subject: [PATCH 6/7] improve availability wording --- Doc/library/intro.rst | 7 ++----- Doc/library/ssl.rst | 8 ++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Doc/library/intro.rst b/Doc/library/intro.rst index 78dd18415ccdd0..e45f6e8155dad2 100644 --- a/Doc/library/intro.rst +++ b/Doc/library/intro.rst @@ -65,11 +65,8 @@ Notes on availability *Availability: Linux >= 3.17 with glibc >= 2.27* requires both Linux 3.17 or newer and glibc 2.27 or newer. -* "Availability" notes may also be used to indicate requirements on how Python - was built. For instance, "Availability: OpenSSL >= 3.5" note means that the - feature is available if Python has been built with OpenSSL 3.5 or later, - while "Availability: not AWS-LC" note means that the feature is not available - if Python has been built with AWS-LC instead of OpenSSL. +* A particular library dependency with an optional minimal ``major.minor`` + version constraint is indicated by *Availability: library >= major.minor*. .. _wasm-availability: diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 883724e1c0d3aa..b917b99b12fadd 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -28,7 +28,15 @@ probably additional platforms, as long as OpenSSL is installed on that platform. cause variations in behavior. For example, TLSv1.3 comes with OpenSSL version 1.1.1. +.. note:: + + Support for other implementations of ``libssl`` such as AWS-LC, BoringSSL, + or LibreSSL, may be assumed but not guaranteed. When a feature is known to + be unavailable for a sepcific backend, it will be explicitly mentioned in + an :ref:`Availability ` note as *Availability: not *. + .. warning:: + Don't use this module without reading the :ref:`ssl-security`. Doing so may lead to a false sense of security, as the default settings of the ssl module are not necessarily appropriate for your application. From 0db99b97185eef671592c6f49b202ff11a166338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 5 Oct 2025 12:26:39 +0200 Subject: [PATCH 7/7] Apply suggestion from @picnixz --- Doc/library/ssl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index b917b99b12fadd..bb75cf38b45c92 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -31,8 +31,8 @@ probably additional platforms, as long as OpenSSL is installed on that platform. .. note:: Support for other implementations of ``libssl`` such as AWS-LC, BoringSSL, - or LibreSSL, may be assumed but not guaranteed. When a feature is known to - be unavailable for a sepcific backend, it will be explicitly mentioned in + or LibreSSL may be assumed but not guaranteed. When a feature is known to + be unavailable for a specific backend, it will be explicitly mentioned in an :ref:`Availability ` note as *Availability: not *. .. warning::