1"""Extract version information from Include/patchlevel.h.""" 2 3import re 4import sys 5from pathlib import Path 6from typing import Literal, NamedTuple 7 8CPYTHON_ROOT = Path( 9 __file__, # cpython/Doc/tools/extensions/patchlevel.py 10 "..", # cpython/Doc/tools/extensions 11 "..", # cpython/Doc/tools 12 "..", # cpython/Doc 13 "..", # cpython 14).resolve() 15PATCHLEVEL_H = CPYTHON_ROOT / "Include" / "patchlevel.h" 16 17RELEASE_LEVELS = { 18 "PY_RELEASE_LEVEL_ALPHA": "alpha", 19 "PY_RELEASE_LEVEL_BETA": "beta", 20 "PY_RELEASE_LEVEL_GAMMA": "candidate", 21 "PY_RELEASE_LEVEL_FINAL": "final", 22} 23 24 25class version_info(NamedTuple): # noqa: N801 26 major: int #: Major release number 27 minor: int #: Minor release number 28 micro: int #: Patch release number 29 releaselevel: Literal["alpha", "beta", "candidate", "final"] 30 serial: int #: Serial release number 31 32 33def get_header_version_info() -> version_info: 34 # Capture PY_ prefixed #defines. 35 pat = re.compile(r"\s*#define\s+(PY_\w*)\s+(\w+)", re.ASCII) 36 37 defines = {} 38 patchlevel_h = PATCHLEVEL_H.read_text(encoding="utf-8") 39 for line in patchlevel_h.splitlines(): 40 if (m := pat.match(line)) is not None: 41 name, value = m.groups() 42 defines[name] = value 43 44 return version_info( 45 major=int(defines["PY_MAJOR_VERSION"]), 46 minor=int(defines["PY_MINOR_VERSION"]), 47 micro=int(defines["PY_MICRO_VERSION"]), 48 releaselevel=RELEASE_LEVELS[defines["PY_RELEASE_LEVEL"]], 49 serial=int(defines["PY_RELEASE_SERIAL"]), 50 ) 51 52 53def format_version_info(info: version_info) -> tuple[str, str]: 54 version = f"{info.major}.{info.minor}" 55 release = f"{info.major}.{info.minor}.{info.micro}" 56 if info.releaselevel != "final": 57 suffix = {"alpha": "a", "beta": "b", "candidate": "rc"} 58 release += f"{suffix[info.releaselevel]}{info.serial}" 59 return version, release 60 61 62def get_version_info(): 63 try: 64 info = get_header_version_info() 65 return format_version_info(info) 66 except OSError: 67 version, release = format_version_info(sys.version_info) 68 print( 69 f"Failed to get version info from Include/patchlevel.h, " 70 f"using version of this interpreter ({release}).", 71 file=sys.stderr, 72 ) 73 return version, release 74 75 76if __name__ == "__main__": 77 short_ver, full_ver = format_version_info(get_header_version_info()) 78 if sys.argv[1:2] == ["--short"]: 79 print(short_ver) 80 else: 81 print(full_ver) 82