Python Snippets

Environment Variable Loader with Type Conversion and Validation

import os
from typing import Optional, TypeVar, Type, Union, List
from pathlib import Path

T = TypeVar('T', str, int, float, bool)

class EnvLoader:
    """Load and validate environment variables with type conversion."""
    
    @staticmethod
    def get_str(key: str, default: Optional[str] = None, required: bool = False) -> Optional[str]:
        """Get string environment variable."""
        value = os.getenv(key, default)
        if required and value is None:
            raise ValueError(f"Required environment variable '{key}' is not set")
        return value
    
    @staticmethod
    def get_int(key: str, default: Optional[int] = None, required: bool = False) -> Optional[int]:
        """Get integer environment variable."""
        value = os.getenv(key)
        if value is None:
            if required:
                raise ValueError(f"Required environment variable '{key}' is not set")
            return default
        try:
            return int(value)
        except ValueError:
            raise ValueError(f"Environment variable '{key}' must be an integer, got '{value}'")
    
    @staticmethod
    def get_float(key: str, default: Optional[float] = None, required: bool = False) -> Optional[float]:
        """Get float environment variable."""
        value = os.getenv(key)
        if value is None:
            if required:
                raise ValueError(f"Required environment variable '{key}' is not set")
            return default
        try:
            return float(value)
        except ValueError:
            raise ValueError(f"Environment variable '{key}' must be a float, got '{value}'")
    
    @staticmethod
    def get_bool(key: str, default: Optional[bool] = None, required: bool = False) -> Optional[bool]:
        """Get boolean environment variable."""
        value = os.getenv(key)
        if value is None:
            if required:
                raise ValueError(f"Required environment variable '{key}' is not set")
            return default
        if value.lower() in ('true', '1', 'yes', 'on'):
            return True
        elif value.lower() in ('false', '0', 'no', 'off'):
            return False
        else:
            raise ValueError(f"Environment variable '{key}' must be a boolean, got '{value}'")
    
    @staticmethod
    def get_list(key: str, separator: str = ',', default: Optional[List[str]] = None, 
                 required: bool = False) -> Optional[List[str]]:
        """Get list environment variable."""
        value = os.getenv(key)
        if value is None:
            if required:
                raise ValueError(f"Required environment variable '{key}' is not set")
            return default or []
        return [item.strip() for item in value.split(separator) if item.strip()]
    
    @staticmethod
    def get_path(key: str, default: Optional[Path] = None, required: bool = False) -> Optional[Path]:
        """Get path environment variable."""
        value = os.getenv(key)
        if value is None:
            if required:
                raise ValueError(f"Required environment variable '{key}' is not set")
            return default
        path = Path(value)
        if not path.exists():
            raise ValueError(f"Path in environment variable '{key}' does not exist: {path}")
        return path

# Example usage
if __name__ == "__main__":
    # Example configuration class
    class Config:
        # Database settings
        DB_HOST = EnvLoader.get_str('DB_HOST', 'localhost')
        DB_PORT = EnvLoader.get_int('DB_PORT', 5432)
        DB_NAME = EnvLoader.get_str('DB_NAME', required=True)
        
        # API settings
        API_KEY = EnvLoader.get_str('API_KEY', required=True)
        DEBUG = EnvLoader.get_bool('DEBUG', False)
        RETRY_COUNT = EnvLoader.get_int('RETRY_COUNT', 3)
        
        # Feature flags
        FEATURES = EnvLoader.get_list('FEATURES', default=['feature1', 'feature2'])
        
        # File paths
        LOG_DIR = EnvLoader.get_path('LOG_DIR', Path('./logs'))
        
    # Usage example
    try:
        config = Config()
        print(f"Database: {config.DB_HOST}:{config.DB_PORT}/{config.DB_NAME}")
        print(f"Debug mode: {config.DEBUG}")
        print(f"Features: {config.FEATURES}")
        print(f"Log directory: {config.LOG_DIR}")
    except ValueError as e:
        print(f"Configuration error: {e}")

What This Code Does

This environment variable loader provides a robust way to manage application configuration using environment variables. It offers:

  1. Type-safe retrieval: Automatically converts environment variables to appropriate Python types (str, int, float, bool, list, Path)
  2. Validation: Ensures values match expected types and formats
  3. Default values: Provides fallback values when environment variables aren’t set
  4. Required field checking: Enforces that critical configuration values are present
  5. Error handling: Gives clear error messages when configuration is invalid

Why This Is Useful

Environment variables are the standard way to configure applications, especially in containerized environments like Docker or cloud platforms. This utility solves common problems:

How to Run This Code

  1. Save the snippet as env_loader.py
  2. Set environment variables in your shell:
    export DB_NAME=myapp
    export API_KEY=secret123
    export DEBUG=true
    export RETRY_COUNT=5
    export FEATURES=auth,cache,metrics
    
  3. Run the script:
    python env_loader.py
    

The code will load the environment variables, convert them to appropriate types, validate them, and print the configuration. If required variables are missing or have invalid values, it will show descriptive error messages.