signal-bot/signal_bot/versioning.py
Krzysztof kuhy Rudnicki ed51aaf3eb release: 0.1.2 «Anicet» — the favourite hour is a real one
Ships the fix in 8fb053b, which had been running unreleased since the
live database was migrated.

!ja was reporting the same hour, 12:00, to every member of the group.
That was the placeholder hour on the 342 messages carried over from the
pre-0.1.0 counter, which have no timestamps — and favourite_hour was the
one query that never excluded the backfilled day, so the synthetic noons
outvoted every real evening. The genuine answer is 19:00-21:00.

The absent hour is now -1 rather than a real-looking 12, and hour
queries filter it out, so forgetting the exclusion can no longer invent
an answer. Somebody whose only messages are backfilled gets no hour line
at all instead of a made-up one.

Exercised in testbot before the bump; kuhy confirmed the release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d48t4qsQLBJnDwDeo2Nom
2026-08-30 20:18:22 +02:00

110 lines
3.4 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 = 11
_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
# The pl.wikipedia article title, which is name plus any "(papież)"
# disambiguator -- so it is the URL slug, and name is not.
title: str
reign: str
portrait: str
latin: 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"]),
title=str(entry["title"]),
reign=str(entry["reign"]),
portrait=str(entry["portrait"]),
latin=str(entry["latin"]),
)
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)