Source code for vibesketch.codenames
"""Release codename catalogue.
vibe-sketch names releases after an organic chemist with a named reaction
and a piece of laboratory equipment. The maintainer selected Dötz’s Flask
for 0.1.2 and later 0.1.x patches (``docs/codenames.md``). An explicit patch
entry starts a new name within its minor line without renaming older versions.
Registration accompanies the release version commit.
This module is the **single source of truth**. Three surfaces read it:
* ``vibe-sketch --version`` (:mod:`vibesketch.cli`)
* the server's ``/api/version`` resource, which the browser's About box and
the Electron desktop shell display
* the documentation site's ``{{codename}}`` substitution (``docs/conf.py``
loads this file standalone, so it must keep importing nothing beyond the
standard library)
``tests/test_codenames.py`` checks that every surface agrees with the
catalogue.
Programmatic API
----------------
``codename_for_version(version)``
Resolve a version string to its codename, or ``None``.
``codename_for_current()``
Convenience: the codename of the running ``vibesketch.__version__``.
``version_label()``
The ``"0.1.2 — Dötz’s Flask"`` string the user-facing surfaces print,
or the bare version while no codename is registered.
"""
from __future__ import annotations
import re
__all__ = [
"RELEASE_CODENAMES",
"codename_for_current",
"codename_for_version",
"version_label",
]
# --- Catalogue -------------------------------------------------------------
#
# Keyed by the first ``X.Y.Z`` version to use a name. Later patches inherit
# the most recent entry in their minor line; entries never rename earlier
# versions. Dev, alpha, beta and rc builds inherit the name of their target.
#
# When selecting a new release name, add the entry here in the same commit
# that bumps ``pyproject.toml`` and ``src/vibesketch/__init__.py``, with a
# comment that says what the release is about and why the name fits.
RELEASE_CODENAMES: dict[str, str] = {
# Historical candidates keep the name assigned before the scheme changed.
"0.1.0": "Couper's Coati",
# Corrected desktop delivery starts the organic chemist/equipment scheme.
# Karl Heinz Dötz: Dötz benzannulation; see docs/codenames.md.
"0.1.2": "Dötz’s Flask",
# Otto Diels: the Diels–Alder reaction; predictable geometry and scheme layout.
"0.3.0": "Diels’s Burette",
# Georg Wittig: the Wittig reaction, which interchanges a carbonyl for an alkene;
# this release interchanges drawings with QVF, CDXML and LaTeX.
"0.4.0": "Wittig’s Pipette",
# Richard Heck: traceable reaction calculations and their energy tables.
"0.5.0": "Heck’s Beaker",
# Staged workflows funnel verified artifacts into the release-paper baseline.
"0.6.0": "Negishi’s Funnel",
}
_DEV_SUFFIX = re.compile(
r"""
(?:\.dev\d+ | a\d+ | b\d+ | rc\d+) # PEP-440 pre-release / dev segments
(?:\+.*)?$ # ... and any local version label
""",
re.VERBOSE,
)
_LOCAL_SUFFIX = re.compile(r"\+.*$")
def _strip_dev_suffix(version: str) -> str:
"""Reduce a PEP-440 version to the ``X.Y.Z`` release it belongs to.
``0.2.0.dev3`` -> ``0.2.0``, ``0.2.0rc1`` -> ``0.2.0``,
``0.1.2+local.1`` -> ``0.1.2``.
"""
return _DEV_SUFFIX.sub("", _LOCAL_SUFFIX.sub("", version.strip()))
[docs]
def codename_for_version(version: str) -> str | None:
"""Return the codename for a vibe-sketch release version, or ``None``.
Resolution order:
1. Strip any PEP-440 ``.devN`` / ``aN`` / ``bN`` / ``rcN`` suffix and
local version label, so a dev build inherits the codename of the
release it leads up to.
2. Direct lookup of the resulting ``X.Y.Z`` in :data:`RELEASE_CODENAMES`.
3. Use the most recent named version no later than ``X.Y.Z`` in the
same minor line, so subsequent patches inherit a patch-level change.
4. ``None`` when no codename is registered for that minor.
"""
stripped = _strip_dev_suffix(version)
if stripped in RELEASE_CODENAMES:
return RELEASE_CODENAMES[stripped]
parts = stripped.split(".")
if len(parts) >= 2:
anchor = f"{parts[0]}.{parts[1]}.0"
if len(parts) == 3 and all(part.isdigit() for part in parts):
prefix = f"{parts[0]}.{parts[1]}."
patch = int(parts[2])
eligible = [
(int(key[len(prefix) :]), name)
for key, name in RELEASE_CODENAMES.items()
if key.startswith(prefix)
and key[len(prefix) :].isdigit()
and int(key[len(prefix) :]) <= patch
]
if eligible:
return max(eligible, key=lambda entry: entry[0])[1]
if anchor in RELEASE_CODENAMES:
return RELEASE_CODENAMES[anchor]
return None
[docs]
def codename_for_current() -> str | None:
"""Return the codename of the running vibe-sketch, or ``None``.
Imported lazily so this module stays importable with nothing but the
standard library: ``docs/conf.py`` loads it by path, outside the package.
"""
from vibesketch import __version__
return codename_for_version(__version__)
[docs]
def version_label(version: str | None = None) -> str:
"""Format the version string the user-facing surfaces print.
``"0.1.2 — Dötz’s Flask"`` when a codename is registered, and the bare
version when one is not, so an unnamed line never prints a dangling dash.
"""
if version is None:
from vibesketch import __version__
version = __version__
name = codename_for_version(version)
return f"{version} — {name}" if name else version