mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
docs: Move the python libraries to tools/lib/python
"scripts/lib" was always a bit of an awkward place for Python modules. We already have tools/lib; create a tools/lib/python, move the libraries there, and update the users accordingly. While at it, move the contents of tools/docs/lib. Rather than make another directory, just put these documentation-oriented modules under "kdoc". Signed-off-by: Jonathan Corbet <corbet@lwn.net> Message-ID: <20251110220430.726665-2-corbet@lwn.net>
This commit is contained in:
178
tools/lib/python/kdoc/python_version.py
Normal file
178
tools/lib/python/kdoc/python_version.py
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (c) 2017-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
|
||||
"""
|
||||
Handle Python version check logic.
|
||||
|
||||
Not all Python versions are supported by scripts. Yet, on some cases,
|
||||
like during documentation build, a newer version of python could be
|
||||
available.
|
||||
|
||||
This class allows checking if the minimal requirements are followed.
|
||||
|
||||
Better than that, PythonVersion.check_python() not only checks the minimal
|
||||
requirements, but it automatically switches to a the newest available
|
||||
Python version if present.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from glob import glob
|
||||
from textwrap import indent
|
||||
|
||||
class PythonVersion:
|
||||
"""
|
||||
Ancillary methods that checks for missing dependencies for different
|
||||
types of types, like binaries, python modules, rpm deps, etc.
|
||||
"""
|
||||
|
||||
def __init__(self, version):
|
||||
"""Ïnitialize self.version tuple from a version string"""
|
||||
self.version = self.parse_version(version)
|
||||
|
||||
@staticmethod
|
||||
def parse_version(version):
|
||||
"""Convert a major.minor.patch version into a tuple"""
|
||||
return tuple(int(x) for x in version.split("."))
|
||||
|
||||
@staticmethod
|
||||
def ver_str(version):
|
||||
"""Returns a version tuple as major.minor.patch"""
|
||||
return ".".join([str(x) for x in version])
|
||||
|
||||
@staticmethod
|
||||
def cmd_print(cmd, max_len=80):
|
||||
cmd_line = []
|
||||
|
||||
for w in cmd:
|
||||
w = shlex.quote(w)
|
||||
|
||||
if cmd_line:
|
||||
if not max_len or len(cmd_line[-1]) + len(w) < max_len:
|
||||
cmd_line[-1] += " " + w
|
||||
continue
|
||||
else:
|
||||
cmd_line[-1] += " \\"
|
||||
cmd_line.append(w)
|
||||
else:
|
||||
cmd_line.append(w)
|
||||
|
||||
return "\n ".join(cmd_line)
|
||||
|
||||
def __str__(self):
|
||||
"""Returns a version tuple as major.minor.patch from self.version"""
|
||||
return self.ver_str(self.version)
|
||||
|
||||
@staticmethod
|
||||
def get_python_version(cmd):
|
||||
"""
|
||||
Get python version from a Python binary. As we need to detect if
|
||||
are out there newer python binaries, we can't rely on sys.release here.
|
||||
"""
|
||||
|
||||
kwargs = {}
|
||||
if sys.version_info < (3, 7):
|
||||
kwargs['universal_newlines'] = True
|
||||
else:
|
||||
kwargs['text'] = True
|
||||
|
||||
result = subprocess.run([cmd, "--version"],
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
**kwargs, check=False)
|
||||
|
||||
version = result.stdout.strip()
|
||||
|
||||
match = re.search(r"(\d+\.\d+\.\d+)", version)
|
||||
if match:
|
||||
return PythonVersion.parse_version(match.group(1))
|
||||
|
||||
print(f"Can't parse version {version}")
|
||||
return (0, 0, 0)
|
||||
|
||||
@staticmethod
|
||||
def find_python(min_version):
|
||||
"""
|
||||
Detect if are out there any python 3.xy version newer than the
|
||||
current one.
|
||||
|
||||
Note: this routine is limited to up to 2 digits for python3. We
|
||||
may need to update it one day, hopefully on a distant future.
|
||||
"""
|
||||
patterns = [
|
||||
"python3.[0-9][0-9]",
|
||||
"python3.[0-9]",
|
||||
]
|
||||
|
||||
python_cmd = []
|
||||
|
||||
# Seek for a python binary newer than min_version
|
||||
for path in os.getenv("PATH", "").split(":"):
|
||||
for pattern in patterns:
|
||||
for cmd in glob(os.path.join(path, pattern)):
|
||||
if os.path.isfile(cmd) and os.access(cmd, os.X_OK):
|
||||
version = PythonVersion.get_python_version(cmd)
|
||||
if version >= min_version:
|
||||
python_cmd.append((version, cmd))
|
||||
|
||||
return sorted(python_cmd, reverse=True)
|
||||
|
||||
@staticmethod
|
||||
def check_python(min_version, show_alternatives=False, bail_out=False,
|
||||
success_on_error=False):
|
||||
"""
|
||||
Check if the current python binary satisfies our minimal requirement
|
||||
for Sphinx build. If not, re-run with a newer version if found.
|
||||
"""
|
||||
cur_ver = sys.version_info[:3]
|
||||
if cur_ver >= min_version:
|
||||
ver = PythonVersion.ver_str(cur_ver)
|
||||
return
|
||||
|
||||
python_ver = PythonVersion.ver_str(cur_ver)
|
||||
|
||||
available_versions = PythonVersion.find_python(min_version)
|
||||
if not available_versions:
|
||||
print(f"ERROR: Python version {python_ver} is not spported anymore\n")
|
||||
print(" Can't find a new version. This script may fail")
|
||||
return
|
||||
|
||||
script_path = os.path.abspath(sys.argv[0])
|
||||
|
||||
# Check possible alternatives
|
||||
if available_versions:
|
||||
new_python_cmd = available_versions[0][1]
|
||||
else:
|
||||
new_python_cmd = None
|
||||
|
||||
if show_alternatives and available_versions:
|
||||
print("You could run, instead:")
|
||||
for _, cmd in available_versions:
|
||||
args = [cmd, script_path] + sys.argv[1:]
|
||||
|
||||
cmd_str = indent(PythonVersion.cmd_print(args), " ")
|
||||
print(f"{cmd_str}\n")
|
||||
|
||||
if bail_out:
|
||||
msg = f"Python {python_ver} not supported. Bailing out"
|
||||
if success_on_error:
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(msg)
|
||||
|
||||
print(f"Python {python_ver} not supported. Changing to {new_python_cmd}")
|
||||
|
||||
# Restart script using the newer version
|
||||
args = [new_python_cmd, script_path] + sys.argv[1:]
|
||||
|
||||
try:
|
||||
os.execv(new_python_cmd, args)
|
||||
except OSError as e:
|
||||
sys.exit(f"Failed to restart with {new_python_cmd}: {e}")
|
||||
Reference in New Issue
Block a user