orchestrata_ultimate.core.banned_libraries

Reference › orchestrata_ultimate › core › banned_libraries

The one reader of the banned-dependency registry, .claude/banned-libraries.txt. Before it existed, checkers kept their own hardcoded ban lists, which drifted from the registry (one codemod review check omitted several banned packages while its message claimed to read the file). Now the gates (differential and fact_extractor), the codemod review and a test-purity script all call it.

load_banned_libraries(path) returns (name, rationale) pairs; the comment block immediately above a run of entries becomes that run's rationale, clamped to 400 characters, so a finding can say why a package is banned. A missing registry gives an empty list rather than an error. normalise_package folds - and _ together, banned_names returns the normalised name set, ban_targets lists the names an import could match by, and is_banned checks an import at whichever granularity the registry declares.

At a glance

Source src/orchestrata_ultimate/core/banned_libraries.py
Lines of code 118
Classes 0
Functions 5
Imports (internal) none
Imported by orchestrata_ultimate.gates.differential, orchestrata_ultimate.gates.fact_extractor, orchestrata_ultimate.refactor.codemod.review, scripts.audit_test_purity
Tests that import it tests/core/test_banned_libraries.py, tests/generated_core/test_banned_libraries_generated.py

Class diagram

No classes; see Functions.

Dependency diagram

Dependency diagram orchestrata_ultimate.core.banned_libraries

API

Functions

Function Signature Summary
load_banned_libraries load_banned_libraries(path: Path) -> list[tuple[str, str]] Return (name, rationale) for every entry in the registry.
normalise_package normalise_package(name: str) -> str PyPI treats - and _ as the same character in a distribution name.
banned_names banned_names(path: Path) -> set[str] Just the names, normalised — for callers that do not need rationales.
ban_targets ban_targets(module: str) -> tuple[str, ...] The names a ban registry could match this import by.
is_banned is_banned(module: str, banned: set[str] | frozenset[str]) -> bool Is this import banned, at whichever granularity the registry declared?

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/core/banned_libraries.py

"""The banned-dependency registry, read from one place by everything.

`.claude/banned-libraries.txt` is the single source of truth CLAUDE.md names,
and this module is the single source of truth for READING it. The registry
being shared data is only half the property that matters; a second parser is a
second chance to disagree about what an entry means.

The reason this exists at all was measured, twice, in the same phase:

  - `serialize_codebase_ast.py standards` held `_FORBIDDEN_IMPORTS = {"pyswip"}`
    in code. Nothing else knew pyswip was banned, and the P5 differential is
    the only reason anyone found out before that checker was deleted.
  - `refactor/codemod/review.py` held `("pytest_xdist", "xdist")` in code while
    its own finding message told the reader the list came from
    `.claude/banned-libraries.txt`. It was missing docker, grpcio, grpcio-tools,
    grpc, unittest.mock, mock and pyswip — so an agent's pending edit could
    introduce any of them and the codemod review would pass.

A hardcoded copy does not announce itself. It looks exactly like an enforcement
point that works, right up until the registry gains an entry it never hears
about, and the gap only shows when something compares the two.
"""

from __future__ import annotations

from pathlib import Path

#: Where the registry lives, relative to the repo root.
BANNED_LIBRARIES = Path(".claude/banned-libraries.txt")

#: Rationales are clamped so one verbose entry cannot dominate a fact base or a
#: finding message. Long enough for the reason, short enough to read.
_REASON_LIMIT = 400


def load_banned_libraries(path: Path) -> list[tuple[str, str]]:
    """Return ``(name, rationale)`` for every entry in the registry.

    The comment block immediately ABOVE a run of entries is that run's
    rationale. Carrying it through means a violation can report WHY a package
    is banned rather than only that it is — the difference between an
    actionable finding and a wall.

    A missing registry yields an empty list rather than raising: the callers
    are checkers, and a checker that explodes on a missing config file is worse
    than one that reports nothing, which is visible in its own output.
    """
    path = Path(path)
    if not path.is_file():
        return []

    entries: list[tuple[str, str]] = []
    pending: list[str] = []
    after_entry = False
    for raw in path.read_text(encoding="utf-8").splitlines():
        line = raw.strip()
        if not line:
            continue
        if line.startswith("#"):
            # A comment FOLLOWING entries starts a new block's rationale.
            # Without this reset every reason accumulated the file header plus
            # every earlier block, and the clamp then cut the actual reason off
            # the end — so the first entries carried prose about the wrong
            # package. Measured on `xdist`, whose "deadlocks THIS suite" never
            # survived the truncation.
            if after_entry:
                pending = []
                after_entry = False
            comment = line.lstrip("#").strip()
            if comment.startswith("---"):
                # The registry's own "--- entries ---" divider. Everything above
                # it documents the FILE, not any package.
                pending = []
                continue
            if comment:
                pending.append(comment)
            continue
        name = line.split("==")[0].split(">=")[0].split("<")[0].split("[")[0].strip()
        if name:
            entries.append((name, " ".join(pending)[:_REASON_LIMIT]))
            after_entry = True
    return entries


def normalise_package(name: str) -> str:
    """PyPI treats ``-`` and ``_`` as the same character in a distribution name.

    Mirrors ``normalise_package/2`` in ``gate_rules.pl``.
    """
    return name.replace("-", "_")


def banned_names(path: Path) -> set[str]:
    """Just the names, normalised — for callers that do not need rationales."""
    return {normalise_package(name) for name, _ in load_banned_libraries(path)}


def ban_targets(module: str) -> tuple[str, ...]:
    """The names a ban registry could match this import by.

    Mirrors ``ban_target/3`` in ``gate_rules.pl``. A DOTLESS registry entry
    names a distribution, so the comparison is against the import's top-level
    package and every submodule of it is caught. A DOTTED entry names one
    module inside a package that is otherwise fine — ``unittest.mock`` is
    banned, ``unittest`` is not — so the comparison is against the full path.
    A caller does not know which form the entry took, so it tries both.

    Folding to the top level alone is the bug this replaced: it made
    ``from unittest.mock import patch`` look like an import of ``unittest``,
    which no registry entry matches, so the ban was unenforceable.
    """
    top = module.split(".")[0]
    return (module,) if module == top else (module, top)


def is_banned(module: str, banned: set[str] | frozenset[str]) -> bool:
    """Is this import banned, at whichever granularity the registry declared?"""
    return any(normalise_package(t).lower() in banned for t in ban_targets(module))