|
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. |
8 | 2 |
|
9 |
| - :copyright: 2007-2008 by Georg Brandl. |
10 |
| - :license: Python license. |
| 3 | +Adapted from Doc/tools/getversioninfo. |
11 | 4 | """
|
12 | 5 |
|
13 |
| -from __future__ import print_function |
14 |
| - |
15 |
| -import os |
16 | 6 | import re
|
17 | 7 | 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 | +} |
18 | 26 |
|
19 |
| -def get_header_version_info(srcdir): |
20 |
| - patchlevel_h = os.path.join(srcdir, '..', 'Include', 'patchlevel.h') |
21 | 27 |
|
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) |
25 | 39 |
|
26 | 40 | 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 |
47 | 46 |
|
| 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 | + ) |
48 | 54 |
|
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}' |
55 | 62 | return version, release
|
56 | 63 |
|
57 | 64 |
|
58 | 65 | def get_version_info():
|
59 | 66 | 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) |
65 | 73 | return version, release
|
66 | 74 |
|
| 75 | + |
67 | 76 | if __name__ == '__main__':
|
68 |
| - print(get_header_version_info('.')[1]) |
| 77 | + print(format_version_info(get_header_version_info())[1]) |
0 commit comments