Skip to content

Commit af4cc37

Browse files
committed
pythonGH-121970: Modernise patchlevel
1 parent 7b36b67 commit af4cc37

File tree

2 files changed

+59
-50
lines changed

2 files changed

+59
-50
lines changed

Diff for: Doc/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363

6464
# We look for the Include/patchlevel.h file in the current Python source tree
6565
# and replace the values accordingly.
66-
import patchlevel
66+
import patchlevel # tools/extensions/patchlevel.py
6767
version, release = patchlevel.get_version_info()
6868

6969
rst_epilog = f"""

Diff for: Doc/tools/extensions/patchlevel.py

+58-49
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,77 @@
1-
# -*- coding: utf-8 -*-
2-
"""
3-
patchlevel.py
4-
~~~~~~~~~~~~~
5-
6-
Extract version info from Include/patchlevel.h.
7-
Adapted from Doc/tools/getversioninfo.
1+
"""Extract version info from Include/patchlevel.h.
82
9-
:copyright: 2007-2008 by Georg Brandl.
10-
:license: Python license.
3+
Adapted from Doc/tools/getversioninfo.
114
"""
125

13-
from __future__ import print_function
14-
15-
import os
166
import re
177
import sys
8+
import typing
9+
from pathlib import Path
10+
11+
CPYTHON_ROOT = Path(
12+
__file__, # cpython/Doc/tools/extensions/patchlevel.py
13+
'..', # cpython/Doc/tools/extensions
14+
'..', # cpython/Doc/tools
15+
'..', # cpython/Doc
16+
'..', # cpython
17+
).resolve()
18+
PATCHLEVEL_H = CPYTHON_ROOT / 'Include' / 'patchlevel.h'
19+
20+
RELEASE_LEVELS = {
21+
'PY_RELEASE_LEVEL_ALPHA': 'alpha',
22+
'PY_RELEASE_LEVEL_BETA': 'beta',
23+
'PY_RELEASE_LEVEL_GAMMA': 'candidate',
24+
'PY_RELEASE_LEVEL_FINAL': 'final',
25+
}
1826

19-
def get_header_version_info(srcdir):
20-
patchlevel_h = os.path.join(srcdir, '..', 'Include', 'patchlevel.h')
2127

22-
# This won't pick out all #defines, but it will pick up the ones we
23-
# care about.
24-
rx = re.compile(r'\s*#define\s+([a-zA-Z][a-zA-Z_0-9]*)\s+([a-zA-Z_0-9]+)')
28+
class version_info(typing.NamedTuple):
29+
major: int #: Major release number
30+
minor: int #: Minor release number
31+
micro: int #: Patch release number
32+
releaselevel: typing.Literal['alpha', 'beta', 'candidate', 'final']
33+
serial: int #: Serial release number
34+
35+
36+
def get_header_version_info() -> version_info:
37+
# Capture PY_ prefixed #defines.
38+
pat = re.compile(r'\s*#define\s+(PY_\w*)\s+(\w+)', re.ASCII)
2539

2640
d = {}
27-
with open(patchlevel_h) as f:
28-
for line in f:
29-
m = rx.match(line)
30-
if m is not None:
31-
name, value = m.group(1, 2)
32-
d[name] = value
33-
34-
release = version = '%s.%s' % (d['PY_MAJOR_VERSION'], d['PY_MINOR_VERSION'])
35-
micro = int(d['PY_MICRO_VERSION'])
36-
release += '.' + str(micro)
37-
38-
level = d['PY_RELEASE_LEVEL']
39-
suffixes = {
40-
'PY_RELEASE_LEVEL_ALPHA': 'a',
41-
'PY_RELEASE_LEVEL_BETA': 'b',
42-
'PY_RELEASE_LEVEL_GAMMA': 'rc',
43-
}
44-
if level != 'PY_RELEASE_LEVEL_FINAL':
45-
release += suffixes[level] + str(int(d['PY_RELEASE_SERIAL']))
46-
return version, release
41+
patchlevel_h = PATCHLEVEL_H.read_text(encoding='utf-8')
42+
for line in patchlevel_h.splitlines():
43+
if (m := pat.match(line)) is not None:
44+
name, value = m.groups()
45+
d[name] = value
4746

47+
return version_info(
48+
major=int(d['PY_MAJOR_VERSION']),
49+
minor=int(d['PY_MINOR_VERSION']),
50+
micro=int(d['PY_MICRO_VERSION']),
51+
releaselevel=RELEASE_LEVELS[d['PY_RELEASE_LEVEL']],
52+
serial=int(d['PY_RELEASE_SERIAL']),
53+
)
4854

49-
def get_sys_version_info():
50-
major, minor, micro, level, serial = sys.version_info
51-
release = version = '%s.%s' % (major, minor)
52-
release += '.%s' % micro
53-
if level != 'final':
54-
release += '%s%s' % (level[0], serial)
55+
56+
def format_version_info(info: version_info) -> tuple[str, str]:
57+
version = f'{info.major}.{info.minor}'
58+
release = f'{info.major}.{info.minor}.{info.micro}'
59+
if info.releaselevel != 'final':
60+
suffix = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}
61+
release += f'{suffix[info.releaselevel]}{info.serial}'
5562
return version, release
5663

5764

5865
def get_version_info():
5966
try:
60-
return get_header_version_info('.')
61-
except (IOError, OSError):
62-
version, release = get_sys_version_info()
63-
print('Can\'t get version info from Include/patchlevel.h, ' \
64-
'using version of this interpreter (%s).' % release, file=sys.stderr)
67+
info = get_header_version_info()
68+
return format_version_info(info)
69+
except OSError:
70+
version, release = format_version_info(sys.version_info)
71+
print(f"Can't get version info from Include/patchlevel.h, "
72+
f"using version of this interpreter ({release}).", file=sys.stderr)
6573
return version, release
6674

75+
6776
if __name__ == '__main__':
68-
print(get_header_version_info('.')[1])
77+
print(format_version_info(get_header_version_info())[1])

0 commit comments

Comments
 (0)