Source code for structum_lab.plugins.bootstrap.validators

# src/structum_lab/plugins/bootstrap/validators.py
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 PythonWoods

"""Built-in validators for system bootstrap.

This module provides common validation checks that can be used during
application startup to ensure the runtime environment is correctly configured.

Example:
    >>> from structum_lab.plugins.bootstrap import SystemBootstrapper
    >>> from structum_lab.plugins.bootstrap.validators import PythonVersionValidator
    >>>
    >>> boot = SystemBootstrapper()
    >>> boot.add_validator(PythonVersionValidator(min_version=(3, 11)))
    >>> boot.run_or_exit()
"""

import os
import sys
from pathlib import Path
from typing import Any

from structum_lab.config import get_config
from structum_lab.validation import ValidationContext


[docs] class PythonVersionValidator: """Validates Python version."""
[docs] def __init__(self, min_version: tuple[Any, ...] = (3, 9)) -> None: """Initialize the validator. Args: min_version: Minimum required Python version tuple. """ self.min_version = min_version
[docs] def validate(self, context: ValidationContext) -> None: """Validate Python version meets minimum requirement.""" current = sys.version_info version_str = f"{current.major}.{current.minor}.{current.micro}" passed = current >= self.min_version if passed: context.add_check("Python Version", True, f"Python {version_str} >= {self.min_version}") else: context.add_check( "Python Version", False, f"Found {version_str}, required >= {self.min_version}", )
[docs] class EnvVarValidator: """Validates presence of environment variables."""
[docs] def __init__(self, required: list[str]) -> None: """Initialize the validator. Args: required: List of required environment variable names. """ self.required = required
[docs] def validate(self, context: ValidationContext) -> None: """Validate that all required environment variables are set.""" missing = [var for var in self.required if not os.getenv(var)] if missing: context.add_check("Environment Variables", False, f"Missing: {', '.join(missing)}") else: context.add_check("Environment Variables", True, f"Checked {len(self.required)} vars")
[docs] class DirectoryValidator: """Validates critical directories exist and are writable."""
[docs] def __init__(self, paths: list[str]) -> None: """Initialize the validator. Args: paths: List of directory paths to validate. """ self.paths = paths
[docs] def validate(self, context: ValidationContext) -> None: """Validate that directories exist and are writable.""" failed = [] for p in self.paths: path = Path(p) try: path.mkdir(parents=True, exist_ok=True) # Test write test_file = path / ".write_test" test_file.touch() test_file.unlink() except Exception as e: failed.append(f"{p} ({e})") if failed: context.add_check("Directories", False, f"Failed: {'; '.join(failed)}") else: context.add_check("Directories", True, f"Verified {len(self.paths)} directories")
[docs] class ConfigValidator: """Validates that Structum configuration loads correctly."""
[docs] def validate(self, context: ValidationContext) -> None: """Validate that Structum configuration loads correctly. Args: context: Validation context for recording results. """ try: # Attempt to access config. If provider is set[Any] up, this triggers validation. # If no provider is set[Any], get_config() might return a dummy or fail depending on core # We assume a provider is registered before bootstrap if using this validator. get_config() # Just accessing it is often enough to trigger load # We can also check if it's empty context.add_check("Configuration", True, "Configuration loaded successfully") except Exception as e: context.add_check("Configuration", False, f"Error loading config: {e}")