On this page
Dev-overlay sentinel loading and venv inspection, shared between the ``rlsbl dev sync``/``dev status`` commands and the ``dev-overlay-drift`` check.
#rlsbl.overlay_state
#rlsbl.overlay_state
Dev-overlay sentinel loading and venv inspection, shared between the rlsbl dev sync/dev status commands and the dev-overlay-drift check.
Extracted from commands/dev_sync.py to break the checks/ -> commands/ layering violation: checks/project.py's dev-overlay-drift check needs to read the overlay sentinel and inspect the venv, but the checks layer must not import the commands layer. This module hosts the read-side logic (sentinel loading, venv dist-info inspection, overlay classification) so both layers can consume it without a cross-layer import. The write-side (_write_sentinel) and the sync orchestration stay in commands/dev_sync.py, which is the only writer.
Pure relocation -- no behavior change from the original dev_sync definitions.
#MalformedSentinelError
Raised when the overlay sentinel exists but cannot be parsed or read.
A present-but-corrupt sentinel must never read as "no overlays declared" (which would silently SKIP the drift check and exit dev status 0). The sentinel is regenerable local state, so the remedy is to delete it and re-run rlsbl dev sync. Callers surface this loudly rather than degrading.
#_normalize
def _normalize(name)PEP 503 distribution-name normalization.
#load_sentinel
def load_sentinel(project_root)Read SENTINEL_FILENAME. Returns a list of {"package", "path", "version"} dicts, or None when the sentinel does not exist.
A missing sentinel means no overlays were ever declared -- e.g. a fresh CI checkout, where the gitignored sentinel never existed. That is the honest not-applicable state (skip), never a failure.
A present-but-unparseable sentinel (invalid TOML) or a present-but- unreadable one (OSError) is a hard error (:class:MalformedSentinelError), NEVER a silent empty list: reading corruption as "no overlays" would make the drift check SKIP and dev status exit 0 while overlays may in fact be wiped. Mirrors _load_overlays, which also hard-errors on invalid TOML.
#OverlayModeConflictError
Raised when the overlay declaration and the sentinel disagree.
The two local-only files decide, together, which mode a project's environment is in: neither present is registry mode, both present and agreeing is overlay mode. Any disagreement means neither answer is true, and a command that had to pick one would either wipe the overlays or test against a checkout nobody declared. It is a hard error instead.
#_load_overlay_pairs
def _load_overlay_pairs(project_root, filename)Read package -> absolute checkout path from one of the two overlay files, or None when the file does not exist.
Deliberately lighter than dev_sync._load_overlays: this is the read side, which only needs to know which packages are overlaid and from where. dev_sync keeps the full write-side validation (pyproject name match and the rest), since it is the only writer.
#active_overlays
def active_overlays(project_root)Return the overlays this project's environment is currently running on.
Returns a list of {"package", "path"} dicts in overlay mode, or None in registry mode (neither local-only file present -- CI, and every machine with no overlays). Raises :class:OverlayModeConflictError when the declaration and the sentinel disagree, or when a declared checkout is gone.
The mode is decided by BOTH files, exactly as the sandboxed test runner decides it, so every entry point reads one answer. A command that syncs an environment must consult this before running a bare uv sync: an exact sync reinstalls the locked registry wheel over an editable overlay without a word, and the project would then be tested against released code while its own drift check reports the wipe.
#project_environment
def project_environment(project_root)Return the directory of the environment uv manages for project_root.
uv gives a project exactly ONE environment, and it is not always a .venv beside the project's own pyproject.toml:
- A uv workspace member shares the environment at the WORKSPACE ROOT
(uv sync and uv pip install from inside the member both target it), and has no .venv of its own. Looking only under the member directory reported a perfectly healthy overlay as missing.
UV_PROJECT_ENVIRONMENTrelocates it entirely; a relative value is
resolved against the workspace root. Read here for the same reason: it is uv's own selection of the directory this function has to describe, not rlsbl configuration.
#_site_packages_dirs
def _site_packages_dirs(env_dir)Return the existing site-packages directories inside an environment (one per Python minor version present).
#_read_direct_url
def _read_direct_url(dist)Return (editable, path) from an installed distribution's direct_url.json (PEP 610).
A registry wheel has no direct_url.json -> (False, None). An editable install writes dir_info.editable = true and a file:// url pointing at the checkout -> (True, "/abs/checkout"). A non-editable local install -> (False, "/abs/path").
This record is the only honest evidence of editability. An editable install does NOT put a package directory in site-packages -- it writes a .pth import hook whose name and content vary by build backend -- so nothing in the file layout may be used to decide this.
#inspect_installed
def inspect_installed(project_root, package)Inspect the project's environment for how package is installed.
Returns {"found", "editable", "path", "version", "environment"}:
found=False: the environment holds no distribution named package.editable=Truewithpath: an editable install;pathis the
file:// checkout its direct_url.json points at.
editable=False: a registry wheel or non-editable install -- i.e. the
overlay was wiped.
environment: the environment directory that was inspected, so a
not-found answer can say where it looked.
Distributions are read through importlib.metadata pointed at the environment's site-packages, so dist-info naming, metadata parsing and the PEP 610 record all follow the packaging standard rather than a hand-rolled guess at the on-disk layout.
#classify_overlay
def classify_overlay(entry, installed)Compare a sentinel entry against the installed venv state.
Returns (state, detail) where state is OVERLAY_HEALTHY / OVERLAY_WIPED / OVERLAY_MISSING and detail is a human-readable line naming the package and the exact rlsbl dev sync remediation.