Skip to content

Context

provide.foundation.context

TODO: Add module docstring.

Classes

CLIContext

Bases: RuntimeConfig

Runtime context for CLI execution and state management.

Manages CLI-specific settings, output formatting, and runtime state during command execution. Supports loading from files, environment variables, and programmatic updates during CLI command execution.

Attributes
logger property
logger: Any

Get or create a logger for this context.

Functions
__attrs_post_init__
__attrs_post_init__() -> None

Post-initialization hook.

Source code in provide/foundation/context/core.py
def __attrs_post_init__(self) -> None:
    """Post-initialization hook."""
copy
copy() -> CLIContext

Create a deep copy of the context.

Source code in provide/foundation/context/core.py
def copy(self) -> CLIContext:
    """Create a deep copy of the context."""
    return copy.deepcopy(self)
freeze
freeze() -> None

Freeze context to prevent further modifications.

Source code in provide/foundation/context/core.py
def freeze(self) -> None:
    """Freeze context to prevent further modifications."""
    # Note: With attrs, we can't dynamically freeze an instance
    # This is kept for API compatibility but does nothing
    self._frozen = True
from_dict classmethod
from_dict(
    data: dict[str, Any],
    source: ConfigSource = ConfigSource.RUNTIME,
) -> CLIContext

Create context from dictionary.

Parameters:

Name Type Description Default
data dict[str, Any]

Dictionary with context values

required
source ConfigSource

Source of the configuration data

RUNTIME

Returns:

Type Description
CLIContext

New CLIContext instance

Source code in provide/foundation/context/core.py
@classmethod
def from_dict(cls, data: dict[str, Any], source: ConfigSource = ConfigSource.RUNTIME) -> CLIContext:
    """Create context from dictionary.

    Args:
        data: Dictionary with context values
        source: Source of the configuration data

    Returns:
        New CLIContext instance

    """
    kwargs = {}

    if "log_level" in data:
        kwargs["log_level"] = data["log_level"]
    if "profile" in data:
        kwargs["profile"] = data["profile"]
    if "debug" in data:
        kwargs["debug"] = data["debug"]
    if "json_output" in data:
        kwargs["json_output"] = data["json_output"]
    if data.get("config_file"):
        kwargs["config_file"] = Path(data["config_file"])
    if data.get("log_file"):
        kwargs["log_file"] = Path(data["log_file"])
    if "log_format" in data:
        kwargs["log_format"] = data["log_format"]
    if "no_color" in data:
        kwargs["no_color"] = data["no_color"]
    if "no_emoji" in data:
        kwargs["no_emoji"] = data["no_emoji"]

    return cls(**kwargs)
load_config
load_config(path: str | Path) -> None

Load configuration from file.

Supports TOML, JSON, and YAML formats based on file extension.

Parameters:

Name Type Description Default
path str | Path

Path to configuration file

required
Source code in provide/foundation/context/core.py
def load_config(self, path: str | Path) -> None:
    """Load configuration from file.

    Supports TOML, JSON, and YAML formats based on file extension.

    Args:
        path: Path to configuration file

    """
    # CLIContext is not frozen, so we can modify it
    path = Path(path)
    data = self._load_config_data(path)
    self._update_from_config_data(data)
    self._validate()
merge
merge(
    other: CLIContext, override_defaults: bool = False
) -> CLIContext

Merge with another context, with other taking precedence.

Parameters:

Name Type Description Default
other CLIContext

CLIContext to merge with

required
override_defaults bool

If False, only override if other's value differs from its class default

False

Returns:

Type Description
CLIContext

New merged CLIContext instance

Source code in provide/foundation/context/core.py
def merge(self, other: CLIContext, override_defaults: bool = False) -> CLIContext:
    """Merge with another context, with other taking precedence.

    Args:
        other: CLIContext to merge with
        override_defaults: If False, only override if other's value differs from its class default

    Returns:
        New merged CLIContext instance

    """
    merged_data = self.to_dict()
    other_data = other.to_dict()

    if override_defaults:
        self._merge_with_override(merged_data, other_data)
    else:
        self._merge_without_override(merged_data, other_data)

    return CLIContext.from_dict(merged_data)
save_config
save_config(path: str | Path) -> None

Save configuration to file.

Format is determined by file extension.

Parameters:

Name Type Description Default
path str | Path

Path to save configuration

required
Source code in provide/foundation/context/core.py
def save_config(self, path: str | Path) -> None:
    """Save configuration to file.

    Format is determined by file extension.

    Args:
        path: Path to save configuration

    """
    path = Path(path)
    data = self.to_dict()

    # Remove None values for cleaner output
    data = {k: v for k, v in data.items() if v is not None}

    if path.suffix in (".toml", ".tml"):
        write_toml(path, data)
    elif path.suffix == ".json":
        write_json(path, data, indent=2)
    elif path.suffix in (".yaml", ".yml"):
        write_yaml(path, data, default_flow_style=False)
    elif not path.suffix:
        raise ConfigurationError(
            f"Unsupported config format: no file extension for {path}",
            code="MISSING_FILE_EXTENSION",
            path=str(path),
        )
    else:
        raise ConfigurationError(
            f"Unsupported config format: {path.suffix}",
            code="UNSUPPORTED_CONFIG_FORMAT",
            path=str(path),
            suffix=path.suffix,
        )
to_dict
to_dict(include_sensitive: bool = True) -> dict[str, Any]

Convert context to dictionary.

Source code in provide/foundation/context/core.py
def to_dict(self, include_sensitive: bool = True) -> dict[str, Any]:
    """Convert context to dictionary."""
    return {
        "log_level": self.log_level,
        "profile": self.profile,
        "debug": self.debug,
        "json_output": self.json_output,
        "config_file": str(self.config_file) if self.config_file else None,
        "log_file": str(self.log_file) if self.log_file else None,
        "log_format": self.log_format,
        "no_color": self.no_color,
        "no_emoji": self.no_emoji,
    }
update_from_env
update_from_env(prefix: str = 'PROVIDE') -> None

Update context from environment variables.

Parameters:

Name Type Description Default
prefix str

Environment variable prefix (default: PROVIDE)

'PROVIDE'
Source code in provide/foundation/context/core.py
def update_from_env(self, prefix: str = "PROVIDE") -> None:
    """Update context from environment variables.

    Args:
        prefix: Environment variable prefix (default: PROVIDE)

    """
    if self._frozen:
        raise StateError(
            "Context is frozen and cannot be modified",
            code="CONTEXT_FROZEN",
            context_type=type(self).__name__,
        )

    # Create default instance and environment instance
    default_ctx = self.__class__()  # All defaults
    env_ctx = self.from_env(prefix=prefix)  # Environment + defaults

    # Only update fields where environment differs from default
    for attr in fields(self.__class__):
        if not attr.name.startswith("_"):  # Skip private fields
            default_value = getattr(default_ctx, attr.name)
            env_value = getattr(env_ctx, attr.name)

            # If environment value differs from default, it came from env
            if env_value != default_value:
                setattr(self, attr.name, env_value)