mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-11 14:27:50 +01:00
Some checks are pending
CI / check (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
104 lines
3.2 KiB
Python
104 lines
3.2 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence.
|
|
"""Release versions named after the popes, in order.
|
|
|
|
Release *n* is pope *n*: 0.0.0 is Święty Piotr Apostoł, and the version is a
|
|
pure function of the ordinal rather than a judgement about the size of a
|
|
change. The one fixed point is Jan Paweł II at 2.13.7, which is what picks
|
|
the two constants below -- eight patches per minor, and majors opening at
|
|
ordinals 33 and 153:
|
|
|
|
(264 - 153) = 111 = 13 * 8 + 7 -> 2.13.7
|
|
|
|
Both earlier majors happen to divide evenly by eight as well (32 = 4 * 8,
|
|
120 = 15 * 8), so no bracket needs a fudge factor. The anchors are not
|
|
arbitrary either: 33 is Sylwester I, pope through the Edict of Milan and
|
|
Nicaea, and 153 is Wiktor II, the first pope elected after the 1054 schism.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any, Final
|
|
|
|
# Ordinals at which a new major version opens. Ascending, first entry 1.
|
|
MAJOR_STARTS: Final = (1, 33, 153)
|
|
PATCHES_PER_MINOR: Final = 8
|
|
|
|
# Which release this build is. Bump by one per release; everything else,
|
|
# including the version string in pyproject.toml, follows from it.
|
|
RELEASE_ORDINAL: Final = 5
|
|
|
|
_POPES_FILE: Final = Path(__file__).with_name("popes.json")
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class Pope:
|
|
"""One entry of the papal list, as the version ladder needs it."""
|
|
|
|
ordinal: int
|
|
name: str
|
|
reign: str
|
|
portrait: str
|
|
|
|
|
|
def _load() -> tuple[Pope, ...]:
|
|
"""Read popes.json once, at import."""
|
|
document: dict[str, Any] = json.loads(_POPES_FILE.read_text(encoding="utf-8"))
|
|
return tuple(
|
|
Pope(
|
|
ordinal=int(entry["ordinal"]),
|
|
name=str(entry["name"]),
|
|
reign=str(entry["reign"]),
|
|
portrait=str(entry["portrait"]),
|
|
)
|
|
for entry in document["popes"]
|
|
)
|
|
|
|
|
|
POPES: Final = _load()
|
|
|
|
|
|
def version_for(ordinal: int) -> str:
|
|
"""Return the dotted version string for the given papal ordinal.
|
|
|
|
Raises:
|
|
ValueError: if the ordinal is outside the known list.
|
|
"""
|
|
if not 1 <= ordinal <= len(POPES):
|
|
message = f"no pope with ordinal {ordinal}; the list has {len(POPES)}"
|
|
raise ValueError(message)
|
|
major = max(index for index, start in enumerate(MAJOR_STARTS) if ordinal >= start)
|
|
offset = ordinal - MAJOR_STARTS[major]
|
|
return f"{major}.{offset // PATCHES_PER_MINOR}.{offset % PATCHES_PER_MINOR}"
|
|
|
|
|
|
def pope_for(ordinal: int) -> Pope:
|
|
"""Return the pope with the given ordinal.
|
|
|
|
Raises:
|
|
ValueError: if the ordinal is outside the known list.
|
|
"""
|
|
if not 1 <= ordinal <= len(POPES):
|
|
message = f"no pope with ordinal {ordinal}; the list has {len(POPES)}"
|
|
raise ValueError(message)
|
|
return POPES[ordinal - 1]
|
|
|
|
|
|
def ordinal_for(version: str) -> int:
|
|
"""Return the papal ordinal a version string denotes.
|
|
|
|
Raises:
|
|
ValueError: if no release carries that version.
|
|
"""
|
|
for pope in POPES:
|
|
if version_for(pope.ordinal) == version:
|
|
return pope.ordinal
|
|
message = f"no release carries version {version!r}"
|
|
raise ValueError(message)
|
|
|
|
|
|
__version__: Final = version_for(RELEASE_ORDINAL)
|
|
POPE: Final = pope_for(RELEASE_ORDINAL)
|