https://github.com/python/cpython/commit/efc7d1ca866ce7706e4ff5f82a03266f08d27608
commit: efc7d1ca866ce7706e4ff5f82a03266f08d27608
branch: 3.13
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: AA-Turner <9087854+aa-tur...@users.noreply.github.com>
date: 2024-07-20T14:02:23Z
summary:

[3.13] GH-121970: Modernise the patchlevel extension (GH-121995) (#122060)

GH-121970: Modernise the patchlevel extension (GH-121995)
(cherry picked from commit b7ad711fcb37dd001e6bf8466c9503eef6d20331)

Co-authored-by: Adam Turner <9087854+aa-tur...@users.noreply.github.com>

files:
M Doc/.ruff.toml
M Doc/conf.py
M Doc/tools/extensions/patchlevel.py

diff --git a/Doc/.ruff.toml b/Doc/.ruff.toml
index 5c40d90b8f000b..24f1c4f2ff6801 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 dfa873274b69bf..17e98e1a01ed21 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 @@
 
 # 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 617f28c2527ddf..f2df6db47a2227 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])

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to