| """Extract version information from Include/patchlevel.h.""" |
| |
| import re |
| import sys |
| from pathlib import Path |
| from typing import Literal, NamedTuple |
| |
| 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" |
| |
| RELEASE_LEVELS = { |
| "PY_RELEASE_LEVEL_ALPHA": "alpha", |
| "PY_RELEASE_LEVEL_BETA": "beta", |
| "PY_RELEASE_LEVEL_GAMMA": "candidate", |
| "PY_RELEASE_LEVEL_FINAL": "final", |
| } |
| |
| |
| 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 |
| |
| |
| def get_header_version_info() -> version_info: |
| # Capture PY_ prefixed #defines. |
| pat = re.compile(r"\s*#define\s+(PY_\w*)\s+(\w+)", re.ASCII) |
| |
| 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 |
| |
| 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 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: |
| 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(format_version_info(get_header_version_info())[0]) |