Module adcp.canonical_formats.registry

v1↔v2 canonical mapping registry loader + matchers.

Implements the registry contract from registries/v1-canonical-mapping.json. Two match modes:

  • Glob — exact / wildcard match against a v1 format_id.id value. The 3.1.13 registry carries literal entries for observed legacy duration, dimension, VAST, and Retina image naming conventions. The matcher handles * wildcards anywhere in the pattern so additional literal entries work without further code change.
  • Structural — match against the v1 format's slot shape, asset types, and VAST/DAAST version constraints. The primary fallback for v1 wire traffic.

Directional invariant: this registry is authoritative for v1 → v2 projection only. The v2 → v1 path in :mod:adcp.canonical_formats.v2_to_v1 does NOT consult the registry; it relies on the seller-asserted v1_format_ref[] on the v2 declaration.

Functions

def glob_match(value: str, pattern: str) ‑> bool
Expand source code
def glob_match(value: str, pattern: str) -> bool:
    """Glob-match ``value`` against a registry ``format_id_glob`` pattern.

    Per the registry schema: ``*`` matches any segment. Patterns are
    compared against the v1 ``format_id.id`` (NOT the ``{agent_url, id}``
    pair — the registry mantra is family identification, not full
    namespace resolution).

    Treats ``*`` as a permissive wildcard (any chars including ``_``).
    Other regex metacharacters are escaped — the pattern language is
    glob, not regex.

    .. note::
       **Experimental.** This helper is unused on the v2 → v1 path (the
       registry is consulted only on the v1 → v2 inbound path, which
       lands in #741's second PR). The signature may shift when that
       path consumes it; adopters SHOULD pin to the SDK release they
       integrate against.
    """
    if pattern == "*":
        return True
    regex = "^" + re.escape(pattern).replace(r"\*", ".*") + "$"
    return re.fullmatch(regex, value) is not None

Glob-match value against a registry format_id_glob pattern.

Per the registry schema: * matches any segment. Patterns are compared against the v1 format_id.id (NOT the {agent_url, id} pair — the registry mantra is family identification, not full namespace resolution).

Treats * as a permissive wildcard (any chars including _). Other regex metacharacters are escaped — the pattern language is glob, not regex.

Note

Experimental. This helper is unused on the v2 → v1 path (the registry is consulted only on the v1 → v2 inbound path, which lands in #741's second PR). The signature may shift when that path consumes it; adopters SHOULD pin to the SDK release they integrate against.

def load_default_registry() ‑> V1V2CanonicalFormatMappingRegistry
Expand source code
def load_default_registry() -> V1V2CanonicalFormatMappingRegistry:
    """Load and parse the AAO-published v1↔v2 mapping registry.

    Returns a fresh deep copy of the cached parsed registry — callers
    can safely mutate the returned instance without affecting other
    callers in the same process. The underlying parsed registry is
    cached per process (the registry is immutable for a given SDK
    build, keyed by ``ADCP_VERSION``).

    .. note::
       **Experimental.** The registry is consulted only on the v1 → v2
       inbound path (lands in #741 part 2). Adopters using this helper
       to inspect the bundled mappings SHOULD pin to the SDK release
       they integrate against; the return shape may sharpen when the
       inbound consumer lands.

    Raises:
        RegistryLoadError: when the bundle is missing, malformed JSON,
            or fails schema validation.
    """
    return _load_registry_uncopied().model_copy(deep=True)

Load and parse the AAO-published v1↔v2 mapping registry.

Returns a fresh deep copy of the cached parsed registry — callers can safely mutate the returned instance without affecting other callers in the same process. The underlying parsed registry is cached per process (the registry is immutable for a given SDK build, keyed by ADCP_VERSION).

Note

Experimental. The registry is consulted only on the v1 → v2 inbound path (lands in #741 part 2). Adopters using this helper to inspect the bundled mappings SHOULD pin to the SDK release they integrate against; the return shape may sharpen when the inbound consumer lands.

Raises
-----=
RegistryLoadError
when the bundle is missing, malformed JSON, or fails schema validation.
def structural_match(*,
asset_types: list[str],
vast_versions: list[str] | None = None,
daast_versions: list[str] | None = None,
width: int | None = None,
height: int | None = None,
pattern: Any) ‑> bool
Expand source code
def structural_match(
    *,
    asset_types: list[str],
    vast_versions: list[str] | None = None,
    daast_versions: list[str] | None = None,
    width: int | None = None,
    height: int | None = None,
    pattern: Any,
) -> bool:
    """Check whether a v1 format's structural shape matches a registry entry.

    ``pattern`` is the registry entry's ``structural`` block (a
    :class:`adcp.types.V1CanonicalStructural` or equivalent dict). All
    constraints declared on the pattern MUST match; constraints absent
    from the pattern do not narrow the match.

    Args:
        asset_types: Asset types appearing in the v1 format's slots.
            The pattern's ``asset_types`` is a *subset* requirement —
            every type the pattern lists must be present in ``asset_types``.
        vast_versions: VAST version(s) declared on the v1 format (typically
            a single value like ``"4.2"``). Each must satisfy at least one
            constraint in the pattern's ``vast_versions`` list.
        daast_versions: DAAST version(s); same matching semantics as VAST.
        width: Slot dimension width (pixels), if applicable.
        height: Slot dimension height (pixels), if applicable.
        pattern: The registry entry's ``structural`` block.

    Returns:
        ``True`` iff every constraint declared on ``pattern`` is satisfied
        by the v1 format's structural shape.

    .. note::
       **Experimental.** Same caveat as :func:`glob_match` — the v1 → v2
       inbound consumer lands in the second half of #741.
    """
    if hasattr(pattern, "model_dump"):
        p = pattern.model_dump(exclude_none=True)
    else:
        p = dict(pattern) if pattern else {}

    want_types = p.get("asset_types")
    if want_types:
        for t in want_types:
            if t not in asset_types:
                return False

    want_vast = p.get("vast_versions")
    if want_vast:
        if not vast_versions:
            return False
        if not any(_versions_overlap(v, want_vast) for v in vast_versions):
            return False

    want_daast = p.get("daast_versions")
    if want_daast:
        if not daast_versions:
            return False
        if not any(_versions_overlap(v, want_daast) for v in daast_versions):
            return False

    want_dims = p.get("dimensions") or {}
    if want_dims.get("width") is not None and want_dims["width"] != width:
        return False
    if want_dims.get("height") is not None and want_dims["height"] != height:
        return False

    return True

Check whether a v1 format's structural shape matches a registry entry.

pattern is the registry entry's structural block (a :class:adcp.types.V1CanonicalStructural or equivalent dict). All constraints declared on the pattern MUST match; constraints absent from the pattern do not narrow the match.

Args
-----=
asset_types
Asset types appearing in the v1 format's slots. The pattern's asset_types is a subset requirement — every type the pattern lists must be present in asset_types.
vast_versions
VAST version(s) declared on the v1 format (typically a single value like "4.2"). Each must satisfy at least one constraint in the pattern's vast_versions list.
daast_versions
DAAST version(s); same matching semantics as VAST.
width
Slot dimension width (pixels), if applicable.
height
Slot dimension height (pixels), if applicable.
pattern
The registry entry's structural block.

Returns -----= True iff every constraint declared on pattern is satisfied by the v1 format's structural shape.

Note

Experimental. Same caveat as :func:glob_match() — the v1 → v2 inbound consumer lands in the second half of #741.

Classes

class RegistryLoadError (*args, **kwargs)
Expand source code
class RegistryLoadError(RuntimeError):
    """Raised when the bundled v1↔v2 registry cannot be loaded or parsed.

    Wraps the underlying :class:`FileNotFoundError`,
    :class:`json.JSONDecodeError`, or :class:`pydantic.ValidationError`
    with a contextual message naming the registry path + ADCP version so
    adopters can diagnose a corrupt bundle.
    """

Raised when the bundled v1↔v2 registry cannot be loaded or parsed.

Wraps the underlying :class:FileNotFoundError, :class:json.JSONDecodeError, or :class:pydantic.ValidationError with a contextual message naming the registry path + ADCP version so adopters can diagnose a corrupt bundle.

Ancestors

  • builtins.RuntimeError
  • builtins.Exception
  • builtins.BaseException