Source code for calibrated_explanations.api.params
"""Parameter canonicalization utilities (ADR-002).
This module centralizes lightweight argument normalization and consistency
checks, enabling downstream validators and plugins to rely on a stable
parameter contract.
Notes
-----
- Alias mapping enables backward compatibility without behavior drift.
- Combination validation enforces ADR-compliant constraints (e.g., conflicting
parameter exclusivity).
- Canonicalization only maps known aliases to canonical keys when the
canonical key is not already provided.
See ADR-002 for context.
"""
from __future__ import annotations
from typing import Any
from ..utils.exceptions import ConfigurationError
# Removed aliases (v0.11.0).
REMOVED_ALIAS_MAP: dict[str, str] = {
"alpha": "low_high_percentiles",
"alphas": "low_high_percentiles",
"n_jobs": "parallel_workers",
}
# Kept for API compatibility; no active alias mapping remains after v0.11.0.
ALIAS_MAP: dict[str, str] = {}
# Parameter combinations that are mutually exclusive or conflicting.
EXCLUSIVE_PARAM_GROUPS: list[tuple[str, ...]] = [
# Example: threshold and confidence_level are alternative ways to specify coverage.
# Callers should choose one or the other, not both.
("threshold", "confidence_level"),
]
[docs]
def canonicalize_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
"""Return a copy of kwargs with known aliases mapped to canonical keys.
Notes
-----
- If an alias exists in ``kwargs`` and the canonical key is absent, copy the
value to the canonical key.
- If both alias and canonical are present, keep the canonical value and do not
overwrite it.
- Always preserve original keys; we do not delete aliases to avoid
any chance of behavior drift. Callers should read canonical keys first.
- Unknown keys are left untouched.
"""
return dict(kwargs)
[docs]
def reject_removed_aliases(kwargs: dict[str, Any]) -> None:
"""Reject aliases removed in v0.11.0 with actionable migration guidance."""
used = {alias: canonical for alias, canonical in REMOVED_ALIAS_MAP.items() if alias in kwargs}
if not used:
return
formatted = ", ".join(f"'{alias}' -> '{canonical}'" for alias, canonical in used.items())
raise ConfigurationError(
"Deprecated parameter aliases were removed in v0.11.0. "
f"Use canonical names instead: {formatted}.",
details={
"removed_aliases": list(used.keys()),
"canonical_replacements": used,
"removed_in": "v0.11.0",
},
)
[docs]
def validate_param_combination(kwargs: dict[str, Any]) -> None:
"""Perform basic consistency checks for parameter combinations (ADR-002).
Enforces mutual exclusivity and conflict constraints. Raises
``ConfigurationError`` when violations are detected.
Parameters
----------
kwargs : dict
Keyword arguments to validate.
Raises
------
ConfigurationError
When conflicting parameter combinations are detected.
"""
# Check mutual exclusivity groups
for param_group in EXCLUSIVE_PARAM_GROUPS:
present = [p for p in param_group if p in kwargs and kwargs[p] is not None]
if len(present) > 1:
raise ConfigurationError(
f"Parameters {present} are mutually exclusive; specify at most one.",
details={
"conflict": param_group,
"provided": present,
"requirement": "choose one or none",
},
)
__all__ = [
"ALIAS_MAP",
"REMOVED_ALIAS_MAP",
"canonicalize_kwargs",
"reject_removed_aliases",
"validate_param_combination",
]
[docs]
def warn_on_aliases(kwargs: dict[str, Any]) -> None:
"""Compatibility wrapper for the removed alias guard.
Notes
-----
- `warn_on_aliases` historically emitted deprecation warnings.
- Since aliases were removed in v0.11.0, this now fails fast.
"""
reject_removed_aliases(kwargs)
__all__.append("warn_on_aliases")