Source code for timesmith.core.validate

"""Input validation for time series estimators.

Validation should happen once at public API boundaries only.
Do not validate inside inner loops.
"""

import logging
from typing import Any

from timesmith.exceptions import ValidationError
from timesmith.typing.validators import assert_panel, assert_series, assert_table

logger = logging.getLogger(__name__)


[docs] def validate_input( data: Any, scitype: str, name: str = "data", allow_none: bool = False, ) -> None: """Validate input data matches expected scitype. Args: data: Data to validate. scitype: Expected scitype ("SeriesLike", "PanelLike", or "TableLike"). name: Name of the variable for error messages. allow_none: If True, None values are allowed. Raises: TypeError: If data doesn't match expected scitype. ValueError: If data is None and allow_none is False. """ if data is None: if not allow_none: raise ValidationError( f"{name} cannot be None", context={"name": name, "scitype": scitype, "allow_none": allow_none}, ) return scitype = scitype.lower() try: if scitype == "serieslike": assert_series(data, name=name) elif scitype == "panellike": assert_panel(data, name=name) elif scitype == "tablelike": assert_table(data, name=name) else: raise ValidationError( f"Unknown scitype: {scitype}. Must be one of " "'SeriesLike', 'PanelLike', 'TableLike'", context={"name": name, "scitype": scitype}, ) except (TypeError, ValueError) as e: # Wrap validation errors from validators with context raise ValidationError( str(e), context={ "name": name, "scitype": scitype, "original_error": type(e).__name__, }, ) from e