Source code for loom.etl.declarative.target._history._errors
"""Historify runtime/domain error types."""
from __future__ import annotations
[docs]
class HistorifyKeyConflictError(ValueError):
"""Raised when the incoming snapshot contains duplicate ``keys + track`` combinations.
SCD2 semantics require that each entity state vector is unique within a
snapshot. Duplicates indicate a data-modelling error: the distinguishing
dimension must be included in ``keys`` or ``track``.
If the same entity legitimately belongs to multiple simultaneous states
(e.g. a player on loan to two clubs at once), include the distinguishing
column in ``track`` — both vectors will then be tracked independently.
Args:
duplicates: Human-readable representation of the conflicting key tuples.
"""
def __init__(self, duplicates: str) -> None:
super().__init__(
f"IntoHistory: duplicate entity state vectors found in snapshot — "
f"each (keys + track) combination must be unique. Conflicting rows: {duplicates}. "
f"If the same entity legitimately occupies multiple simultaneous states "
f"(e.g. a player on loan), include the distinguishing column in 'track'."
)
[docs]
class HistorifyDateCollisionError(ValueError):
"""Raised in LOG mode when two events share the same ``(keys + track, effective_date)``.
At DATE granularity, two events on the same calendar day for the same entity
state vector are ambiguous — relative ordering is lost and idempotency
cannot be guaranteed.
To resolve: switch to ``date_type=\"timestamp\"`` for sub-daily precision, or
deduplicate events upstream before loading.
Args:
collisions: Human-readable description of the colliding key/date pairs.
"""
def __init__(self, collisions: str) -> None:
super().__init__(
f"IntoHistory (LOG mode): same-date collisions detected — two events share "
f"the same (keys + track, effective_date) value at DATE granularity. "
f"Use date_type='timestamp' for sub-daily precision, or deduplicate upstream. "
f"Collisions: {collisions}"
)
[docs]
class HistorifyTemporalConflictError(ValueError):
"""Raised when the target contains future-open vectors and re-weave is disabled.
A temporal conflict occurs when ``valid_from > effective_date``, meaning the
incoming data must be inserted *before* an already-committed record. This
requires explicit opt-in via ``allow_temporal_rerun=True``.
Args:
min_conflict_date: Earliest ``valid_from`` date found in the conflict.
effective_date: The ``effective_date`` of the current run.
"""
def __init__(self, min_conflict_date: object, effective_date: object) -> None:
super().__init__(
f"IntoHistory: temporal conflict detected — the target table contains records "
f"with valid_from={min_conflict_date!r} after the current "
f"effective_date={effective_date!r}. "
f"Set allow_temporal_rerun=True to enable re-weave of historical data."
)
__all__ = [
"HistorifyKeyConflictError",
"HistorifyDateCollisionError",
"HistorifyTemporalConflictError",
]