summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/.ruff.toml1
-rw-r--r--Doc/conf.py6
-rw-r--r--Doc/tools/extensions/patchlevel.py109
3 files changed, 62 insertions, 54 deletions
diff --git a/Doc/.ruff.toml b/Doc/.ruff.toml
index 5c40d90..24f1c4f 100644
--- a/Doc/.ruff.toml
+++ b/Doc/.ruff.toml
@@ -6,7 +6,6 @@ extend-exclude = [
"includes/*",
# Temporary exclusions:
"tools/extensions/escape4chm.py",
- "tools/extensions/patchlevel.py",
"tools/extensions/pyspecific.py",
]
diff --git a/Doc/conf.py b/Doc/conf.py
index dfa8732..17e98e1 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -6,6 +6,7 @@
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
+import importlib
import os
import sys
import time
@@ -64,9 +65,8 @@ copyright = f"2001-{time.strftime('%Y')}, Python Software Foundation"
# We look for the Include/patchlevel.h file in the current Python source tree
# and replace the values accordingly.
-import patchlevel # noqa: E402
-
-version, release = patchlevel.get_version_info()
+# See Doc/tools/extensions/patchlevel.py
+version, release = importlib.import_module('patchlevel').get_version_info()
rst_epilog = f"""
.. |python_version_literal| replace:: ``Python {version}``
diff --git a/Doc/tools/extensions/patchlevel.py b/Doc/tools/extensions/patchlevel.py
index 617f28c..f2df6db 100644
--- a/Doc/tools/extensions/patchlevel.py
+++ b/Doc/tools/extensions/patchlevel.py
@@ -1,68 +1,77 @@
-# -*- coding: utf-8 -*-
-"""
- patchlevel.py
- ~~~~~~~~~~~~~
+"""Extract version information from Include/patchlevel.h."""
- Extract version info from Include/patchlevel.h.
- Adapted from Doc/tools/getversioninfo.
+import re
+import sys
+from pathlib import Path
+from typing import Literal, NamedTuple
- :copyright: 2007-2008 by Georg Brandl.
- :license: Python license.
-"""
+CPYTHON_ROOT = Path(
+ __file__, # cpython/Doc/tools/extensions/patchlevel.py
+ "..", # cpython/Doc/tools/extensions
+ "..", # cpython/Doc/tools
+ "..", # cpython/Doc
+ "..", # cpython
+).resolve()
+PATCHLEVEL_H = CPYTHON_ROOT / "Include" / "patchlevel.h"
-from __future__ import print_function
+RELEASE_LEVELS = {
+ "PY_RELEASE_LEVEL_ALPHA": "alpha",
+ "PY_RELEASE_LEVEL_BETA": "beta",
+ "PY_RELEASE_LEVEL_GAMMA": "candidate",
+ "PY_RELEASE_LEVEL_FINAL": "final",
+}
-import os
-import re
-import sys
-def get_header_version_info(srcdir):
- patchlevel_h = os.path.join(srcdir, '..', 'Include', 'patchlevel.h')
+class version_info(NamedTuple): # noqa: N801
+ major: int #: Major release number
+ minor: int #: Minor release number
+ micro: int #: Patch release number
+ releaselevel: Literal["alpha", "beta", "candidate", "final"]
+ serial: int #: Serial release number
- # This won't pick out all #defines, but it will pick up the ones we
- # care about.
- rx = re.compile(r'\s*#define\s+([a-zA-Z][a-zA-Z_0-9]*)\s+([a-zA-Z_0-9]+)')
- d = {}
- with open(patchlevel_h) as f:
- for line in f:
- m = rx.match(line)
- if m is not None:
- name, value = m.group(1, 2)
- d[name] = value
+def get_header_version_info() -> version_info:
+ # Capture PY_ prefixed #defines.
+ pat = re.compile(r"\s*#define\s+(PY_\w*)\s+(\w+)", re.ASCII)
- release = version = '%s.%s' % (d['PY_MAJOR_VERSION'], d['PY_MINOR_VERSION'])
- micro = int(d['PY_MICRO_VERSION'])
- release += '.' + str(micro)
+ defines = {}
+ patchlevel_h = PATCHLEVEL_H.read_text(encoding="utf-8")
+ for line in patchlevel_h.splitlines():
+ if (m := pat.match(line)) is not None:
+ name, value = m.groups()
+ defines[name] = value
- level = d['PY_RELEASE_LEVEL']
- suffixes = {
- 'PY_RELEASE_LEVEL_ALPHA': 'a',
- 'PY_RELEASE_LEVEL_BETA': 'b',
- 'PY_RELEASE_LEVEL_GAMMA': 'rc',
- }
- if level != 'PY_RELEASE_LEVEL_FINAL':
- release += suffixes[level] + str(int(d['PY_RELEASE_SERIAL']))
- return version, release
+ return version_info(
+ major=int(defines["PY_MAJOR_VERSION"]),
+ minor=int(defines["PY_MINOR_VERSION"]),
+ micro=int(defines["PY_MICRO_VERSION"]),
+ releaselevel=RELEASE_LEVELS[defines["PY_RELEASE_LEVEL"]],
+ serial=int(defines["PY_RELEASE_SERIAL"]),
+ )
-def get_sys_version_info():
- major, minor, micro, level, serial = sys.version_info
- release = version = '%s.%s' % (major, minor)
- release += '.%s' % micro
- if level != 'final':
- release += '%s%s' % (level[0], serial)
+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":
+ suffix = {"alpha": "a", "beta": "b", "candidate": "rc"}
+ release += f"{suffix[info.releaselevel]}{info.serial}"
return version, release
def get_version_info():
try:
- return get_header_version_info('.')
- except (IOError, OSError):
- version, release = get_sys_version_info()
- print('Can\'t get version info from Include/patchlevel.h, ' \
- 'using version of this interpreter (%s).' % release, file=sys.stderr)
+ info = get_header_version_info()
+ return format_version_info(info)
+ except OSError:
+ version, release = format_version_info(sys.version_info)
+ print(
+ f"Failed to get version info from Include/patchlevel.h, "
+ f"using version of this interpreter ({release}).",
+ file=sys.stderr,
+ )
return version, release
-if __name__ == '__main__':
- print(get_header_version_info('.')[1])
+
+if __name__ == "__main__":
+ print(format_version_info(get_header_version_info())[1])