Source code for replane.types

"""Type definitions for the Replane Python SDK.

This module contains all the data models used by the SDK, implemented using
dataclasses and TypedDict for zero external dependencies.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Literal, TypedDict


# Context type - what users pass to evaluate overrides
class Context(TypedDict, total=False):
    """Runtime context for override evaluation.

    Context is a dictionary of string keys to primitive values.
    Common properties include user IDs, subscription plans, regions, etc.

    Example::

        context: Context = {
            "user_id": "user-123",
            "plan": "premium",
            "region": "us-east",
            "is_beta": True,
        }
    """

    pass  # All keys are optional, any str key maps to ContextValue


# Type alias for context values
ContextValue = str | int | float | bool | None


# Condition types for override evaluation
[docs] @dataclass(frozen=True, slots=True) class PropertyCondition: """A condition that compares a context property against expected values.""" operator: Literal["equals", "in", "not_in", "lt", "lte", "gt", "gte"] property: str expected: Any
[docs] @dataclass(frozen=True, slots=True) class SegmentationCondition: """A condition for percentage-based bucketing (gradual rollouts).""" operator: Literal["segmentation"] property: str from_percentage: float to_percentage: float seed: str
[docs] @dataclass(frozen=True, slots=True) class AndCondition: """Logical AND of multiple conditions.""" operator: Literal["and"] conditions: tuple[Condition, ...]
[docs] @dataclass(frozen=True, slots=True) class OrCondition: """Logical OR of multiple conditions.""" operator: Literal["or"] conditions: tuple[Condition, ...]
[docs] @dataclass(frozen=True, slots=True) class NotCondition: """Logical NOT of a condition.""" operator: Literal["not"] condition: Condition
# Union type for all conditions Condition = PropertyCondition | SegmentationCondition | AndCondition | OrCondition | NotCondition
[docs] @dataclass(frozen=True, slots=True) class Override: """An override rule that returns a specific value when conditions match.""" name: str conditions: tuple[Condition, ...] value: Any
[docs] @dataclass(slots=True) class Config: """A configuration with its base value and override rules. Attributes: name: Unique identifier for this config. value: The base/default value returned when no overrides match. overrides: List of override rules evaluated in order. """ name: str value: Any overrides: tuple[Override, ...] = field(default_factory=tuple)
# Type for config change callbacks ConfigChangeCallback = Any # Will be properly typed in client module def parse_condition(data: dict[str, Any]) -> Condition: """Parse a condition from API response data. Args: data: Raw condition data from the API. Returns: A typed Condition object. Raises: ValueError: If the condition operator is unknown. """ operator = data.get("operator") if operator == "and": return AndCondition( operator="and", conditions=tuple(parse_condition(c) for c in data["conditions"]), ) elif operator == "or": return OrCondition( operator="or", conditions=tuple(parse_condition(c) for c in data["conditions"]), ) elif operator == "not": return NotCondition( operator="not", condition=parse_condition(data["condition"]), ) elif operator == "segmentation": return SegmentationCondition( operator="segmentation", property=data["property"], from_percentage=data["fromPercentage"], to_percentage=data["toPercentage"], seed=data["seed"], ) elif operator in ("equals", "in", "not_in"): return PropertyCondition( operator=operator, property=data["property"], expected=data.get("expected", data.get("value")), ) elif operator == "less_than": return PropertyCondition( operator="lt", property=data["property"], expected=data.get("expected", data.get("value")), ) elif operator == "less_than_or_equal": return PropertyCondition( operator="lte", property=data["property"], expected=data.get("expected", data.get("value")), ) elif operator == "greater_than": return PropertyCondition( operator="gt", property=data["property"], expected=data.get("expected", data.get("value")), ) elif operator == "greater_than_or_equal": return PropertyCondition( operator="gte", property=data["property"], expected=data.get("expected", data.get("value")), ) else: raise ValueError(f"Unknown condition operator: {operator}") def parse_override(data: dict[str, Any]) -> Override: """Parse an override from API response data.""" return Override( name=data["name"], conditions=tuple(parse_condition(c) for c in data["conditions"]), value=data["value"], ) def parse_config(data: dict[str, Any]) -> Config: """Parse a config from API response data.""" return Config( name=data["name"], value=data["value"], overrides=tuple(parse_override(o) for o in data.get("overrides", [])), )