"""Release codename catalogue.
Every vibe-view minor release carries a "Person's Animal" codename, the same
*form* the vibe-qc and vibe-queue series use, so the three products read as one
family. Each product draws from its own pool of people, so no name is ever
claimed twice; vibe-view's pool is visualization, computer graphics and
crystallographic imaging. See ``docs/codenames.md`` for the policy and the
pool, and ``docs/release_process.md`` for where in a cut the entry is added.
This module is the **single source of truth**. Four surfaces read it:
* ``vibe-view --version`` (:mod:`vibeview.cli`)
* the browser viewer's About box (:mod:`vibeview.app`)
* the Electron desktop About box, via the launcher handshake config
(``electron/main.js`` reads ``CONFIG.codename``)
* the documentation site's ``{{codename}}`` substitution (``docs/conf.py``,
which loads this file standalone -- it must therefore keep importing
nothing beyond the standard library)
Before this module existed the string was pasted into all four by hand, which
is a drift the test suite could not see. ``tests/test_release_codenames.py``
now 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 ``vibeview.__version__``.
``version_label()``
The ``"2.15.2 -- Person's Animal"`` string the user-facing surfaces print.
"""
from __future__ import annotations
import re
__all__ = [
"RELEASE_CODENAMES",
"codename_for_current",
"codename_for_version",
"version_label",
]
# --- Catalogue -------------------------------------------------------------
#
# Keyed by the ``X.Y.Z`` release version. Minor releases (``X.Y.0``) are always
# present; a patch release only needs its own entry if it has a distinct theme
# worth surfacing separately, and otherwise inherits its parent minor's name
# via the fallback in :func:`codename_for_version`. Dev / alpha / beta / rc
# builds inherit the codename of the release they lead up to, after the PEP-440
# suffix is stripped.
#
# When cutting a new MINOR release, add the entry here in the same commit that
# bumps ``pyproject.toml`` and ``src/vibeview/__init__.py``.
RELEASE_CODENAMES: dict[str, str] = {
"2.15.0": "Roothaan's Roadrunner", # The name v2.15.2 actually shipped
# with, recorded here as history rather than
# as a pool entry. It predates the split and
# the per-product pools, and it borrows a
# quantum-chemistry figure already used by
# vibe-qc ("Roothaan's Raven", v0.1.0). It
# is in the wild -- in the CLI, the desktop
# About box and a tagged wheel -- so it
# stands, by maintainer decision 2026-09-09.
# Names from v2.16.0 on come from the
# visualization / graphics /
# crystallographic-imaging pool in
# docs/codenames.md.
"2.16.0": "Sayle's Starling", # Roger Sayle, RasMol (1992): the first
# molecular viewer a scientist could simply
# install and run, free, without the program
# that produced the data. That is what
# v2.16.0 is -- vibe-view becoming a product
# you can adopt on its own, with its own
# documentation site, release series and
# procedure. Starling: ordinary, gregarious,
# and its murmurations are the standard
# image of structure emerging from many
# small parts. First name from the
# visualization pool; approved by the
# maintainer on 2026-09-09, ahead of the
# cut, so the artwork brief could be
# written against it.
"2.17.0": "Lorensen's Loon", # William Lorensen: marching cubes (1987)
# and a founder of VTK. For a release
# centred on isosurfaces and volume
# rendering -- his algorithm is what draws
# every density and orbital surface here,
# in the toolkit he helped create, so this
# is the most directly earned name in the
# pool. A loon dives and surfaces.
# v2.17.0 brings TREXIO molecular orbitals
# into that isosurface workflow; it does
# not add a new isosurface algorithm.
"2.18.0": "Richardson's Robin", # Jane Richardson: the ribbon diagram
# (1981). For biomolecular work -- chains,
# residues, secondary structure -- because
# the ribbon is still exactly what the
# viewer draws when a structure section
# carries secondary structure.
"2.19.0": "Phong's Pheasant", # Bui Tuong Phong: the Phong reflection
# model. For a release about materials and
# lighting, anchored in the renderer rather
# than the product -- Phong shading is how
# a molecular scene is lit.
"2.20.0": "Levoy's Lemur", # Marc Levoy: direct volume rendering
# (1988). For transfer functions -- colour
# and opacity taken straight from the data
# with no surface extracted, the other half
# of the volume story from Lorensen's.
"3.0.0": "Levinthal's Lynx", # Cyrus Levinthal: the first interactive
# molecular graphics system (MIT, 1965), on
# hardware that had no business doing it.
# Held for a major version about the
# interactive viewer itself. A lynx is what
# you name the thing that sees in the dark.
#
# v2.17.0 through v3.0.0 were approved by
# the maintainer on 2026-09-09 and relayed
# by the release chat. They are registered,
# not scheduled: a version resolves to its
# name whenever it is cut, and nothing here
# commits to cutting it.
}
_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.
``2.16.0.dev3`` -> ``2.16.0``, ``2.16.0rc1`` -> ``2.16.0``,
``2.15.2+local.1`` -> ``2.15.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-view 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. Fall back to the parent minor, ``f"{X}.{Y}.0"``, so patch releases
inherit without needing an entry of their own.
4. ``None`` when no codename is registered for that minor.
Examples
--------
>>> codename_for_version("2.15.0")
"Roothaan's Roadrunner"
>>> codename_for_version("2.15.2") # patch inherits the minor
"Roothaan's Roadrunner"
>>> codename_for_version("2.15.0.dev1") # dev inherits the release
"Roothaan's Roadrunner"
>>> codename_for_version("99.99.99") is None
True
"""
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 anchor in RELEASE_CODENAMES:
return RELEASE_CODENAMES[anchor]
return None
[docs]
def codename_for_current() -> str | None:
"""Return the codename of the running vibe-view, 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, in a container where ``vibeview`` is not installed.
"""
from vibeview 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.
``"2.15.2 — Roothaan's Roadrunner"`` when a codename is registered, and
the bare version when one is not, so an unnamed dev line never prints a
dangling em dash.
"""
if version is None:
from vibeview import __version__
version = __version__
name = codename_for_version(version)
return f"{version} — {name}" if name else version