[PATCH 1/4] docs: test_doc_build: better handle python version

From: Mauro Carvalho Chehab

Date: Thu Oct 15 2026 - 05:00:04 EST


Make the python version logic more generic to allow adding more
python degradation changes on more recent Sphinx tools.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@xxxxxxxxxx>
---
tools/docs/test_doc_build.py | 70 ++++++++++++++++++++++++++----------
1 file changed, 52 insertions(+), 18 deletions(-)

diff --git a/tools/docs/test_doc_build.py b/tools/docs/test_doc_build.py
index 47b4606569f9..4082317d581d 100755
--- a/tools/docs/test_doc_build.py
+++ b/tools/docs/test_doc_build.py
@@ -11,29 +11,47 @@ and optionally test the build.

import argparse
import asyncio
+import glob
import os.path
-import shutil
+import re
import sys
import time
import subprocess

-# Minimal python version supported by the building system.
+MIN_PYTHON_SUBVER = 9 # Currently, Python 3.9 is the minimal supported version

-PYTHON = os.path.basename(sys.executable)
+RE_VERSION = re.compile(r"python3\.\d+$")

-min_python_bin = None

-for i in range(9, 13):
- p = f"python3.{i}"
- if shutil.which(p):
- min_python_bin = p
- break
+def get_min_python_bin(min_subver):
+ """Get minimal python binary since 3.MIN_PYTHON_SUBVER"""
+ bins = set()

-if not min_python_bin:
- min_python_bin = PYTHON
+ paths = os.environ.get("PATH", "").split(os.pathsep)
+ for d in paths:
+ for p in glob.glob(os.path.join(d, "python3.[0-9]*")):
+ if not RE_VERSION.search(p):
+ continue

-# Starting from 8.0, Python 3.9 is not supported anymore.
-PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON}
+ ver = int(p.split(".")[1])
+ if ver < min_subver:
+ continue
+
+ if os.path.isfile(p) and os.access(p, os.X_OK):
+ bins.add(p)
+
+ if not bins:
+ return None
+
+ return sorted(bins, key=lambda x: (3, int(x.split(".")[1])))[0]
+
+# Minimal python versions. Please notice that for 3.4.3, older python
+# versions would work with Sphinx, but kernel doc build is not compatible
+# with them anymore, due to Sphinx extensions and kernel-doc.
+PYTHON_VER_CHANGES = {
+ (3, 4, 3): (3, MIN_PYTHON_SUBVER),
+ (8, 0, 0): (3, 10),
+}

DEFAULT_VERSIONS_TO_TEST = [
(3, 4, 3), # Minimal supported version
@@ -173,7 +191,7 @@ class AsyncCommands:
self.output = None
self.fp = fp

- def log(self, out, verbose, is_info=True):
+ def log(self, out, verbose=True, is_info=True):
out = out.removesuffix('\n')

if verbose:
@@ -362,8 +380,19 @@ class SphinxVenv:
if not args.verbose:
args.verbose = True

+ cmd = AsyncCommands(fp)
+
cur_requirements = {}
- python_bin = min_python_bin
+ python_bin = get_min_python_bin(MIN_PYTHON_SUBVER)
+ if not python_bin:
+ python_bin = os.path.basename(sys.executable)
+ python_ver = tuple(sys.version_info[:2])
+
+ if python_ver < (3, MIN_PYTHON_SUBVER):
+ sys.exit(f"Python {python_ver[0]}.{python_ver[1]} is too old.")
+ else:
+ ver = int(python_bin.split(".")[1])
+ python_ver = tuple([3, ver])

vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions)

@@ -372,8 +401,14 @@ class SphinxVenv:
new_reqs = SPHINX_REQUIREMENTS[cur_ver]
cur_requirements.update(new_reqs)

- if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715
- python_bin = PYTHON_VER_CHANGES[cur_ver]
+ if cur_ver in PYTHON_VER_CHANGES:
+ new_ver = PYTHON_VER_CHANGES[cur_ver]
+ if python_ver < new_ver:
+ python_bin = get_min_python_bin(new_ver[1])
+
+ if not python_bin:
+ cmd.log(f"Can't check {cur_ver}: {python_bin} not found.")
+ break

if cur_ver not in args.versions:
continue
@@ -390,7 +425,6 @@ class SphinxVenv:
python_bin)

if args.build:
- cmd = AsyncCommands(fp)
cmd.log("\nSummary:", verbose=True)
for ver, elapsed_time in sorted(self.built_time.items()):
cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}",
--
2.56.0