env-factory 1.1.0
A flexible Python package for retrieving environment variables from multiple sources including local environment and AWS Secrets Manager
License: MIT
Requires Python: >=3.8
Keywords: environment variables, aws secrets manager, configuration, secrets, env, dotenv

# env_factory

A flexible Python package for retrieving environment variables from multiple sources including local environment and AWS Secrets Manager.

## Features

- **Multiple Sources**: Support for local environment variables and AWS Secrets Manager
- **Type Safety**: Full type hints and robust error handling
- **Flexible Usage**: Factory pattern and convenience functions for easy integration
- **Case-Insensitive Matching**: Fallback to case-insensitive key matching for AWS secrets
- **Comprehensive Logging**: Debug and warning logs for troubleshooting
- **Error Recovery**: Continues processing even if some keys are missing

## Installation

```bash
pip install env-factory
```


### Loading .env files

To automatically load your `.env` file, add this to the top of your main script:

```python
from dotenv import load_dotenv
load_dotenv()  # This loads variables from .env file

# Now you can use env_factory
from env_factory import get_env_variables
```

## Quick Start

### Step 1: Create your .env file (MANDATORY)

Create a `.env` file in your project root directory:

```bash
# .env file - Place in your project root
API_KEY=my-secret-api-key
DATABASE_URL=postgresql://localhost:5432/myapp
DEBUG=true

# For AWS functionality (REQUIRED)
AWS_ACCOUNT_ID=123456789012
```

### Step 2: Simple Examples

#### Example 1: Get Local Environment Variables

```python
from env_factory import get_env_variables

# This will read from your .env file
config = get_env_variables(['API_KEY', 'DATABASE_URL'])
print(config)
# Output: {'API_KEY': 'my-secret-api-key', 'DATABASE_URL': 'postgresql://localhost:5432/myapp'}

# Check if variables exist
api_key = config.get('API_KEY')
if api_key:
    print(f"API Key found: {api_key}")
else:
    print("API Key not found!")
```

#### Example 2: Simple AWS Secrets Manager Usage

```python
from env_factory import get_env_variables

# Make sure AWS_ACCOUNT_ID is in your .env file!
secrets = get_env_variables(
    keys=['DB_PASSWORD', 'JWT_SECRET'],
    source='aws',
    secret_name='my-app-secrets',
    region='us-east-1',
    role_name='my-app-role'
)

print(f"Database password: {secrets.get('DB_PASSWORD')}")
```

#### Example 3: Real-World Web App Setup

```python
from env_factory import get_env_variables

# Load all your app configuration at startup
app_config = get_env_variables([
    'API_KEY',
    'DATABASE_URL',
    'DEBUG',
    'PORT'
])

# Use the configuration
api_key = app_config['API_KEY']
database_url = app_config['DATABASE_URL']
debug_mode = app_config.get('DEBUG', 'false').lower() == 'true'
port = int(app_config.get('PORT', '8000'))

print(f"Starting app on port {port}, debug mode: {debug_mode}")
```

### Simple Examples

### Complete Working Example

Here's a complete example showing how to use the package:

```python
# main.py
from dotenv import load_dotenv
from env_factory import get_env_variables

# Step 1: Load .env file (MANDATORY)
load_dotenv()

# Step 2: Get your environment variables
config = get_env_variables(['API_KEY', 'DATABASE_URL', 'DEBUG'])

# Step 3: Use the configuration
api_key = config.get('API_KEY')
if not api_key:
    raise ValueError("API_KEY is required but not found in .env file")

database_url = config.get('DATABASE_URL', 'sqlite:///default.db')
debug = config.get('DEBUG', 'false').lower() == 'true'

print(f"API Key: {api_key}")
print(f"Database: {database_url}")
print(f"Debug mode: {debug}")
```

Your `.env` file should look like:
```bash
# .env
API_KEY=sk-1234567890abcdef
DATABASE_URL=postgresql://user:pass@localhost/mydb
DEBUG=true
```

### AWS Example with .env

```python
# aws_example.py
from dotenv import load_dotenv
from env_factory import get_env_variables

# Load .env file (contains AWS_ACCOUNT_ID)
load_dotenv()

try:
    # Get secrets from AWS
    secrets = get_env_variables(
        keys=['DB_PASSWORD', 'JWT_SECRET'],
        source='aws',
        secret_name='production-secrets',
        region='us-east-1',
        role_name='app-secrets-role'
    )
    
    print("✅ Successfully retrieved AWS secrets!")
    print(f"DB Password exists: {'DB_PASSWORD' in secrets}")
    
except Exception as e:
    print(f"❌ Error: {e}")
```

Your `.env` file must include:
```bash
# .env - MANDATORY for AWS
AWS_ACCOUNT_ID=123456789012
```

### Error Handling Example

```python
from dotenv import load_dotenv
from env_factory import get_env_variables, ConfigurationError

load_dotenv()

try:
    config = get_env_variables(['REQUIRED_API_KEY'])
    
    api_key = config.get('REQUIRED_API_KEY')
    if not api_key:
        print("⚠️  Warning: REQUIRED_API_KEY not found in .env file")
        print("Please add it to your .env file: REQUIRED_API_KEY=your-key-here")
    else:
        print("✅ API key loaded successfully!")
        
except ConfigurationError as e:
    print(f"❌ Configuration error: {e}")
``` with Classes

```python
from env_factory import LocalEnvRetrieval, AWSEnvRetrieval

# Local environment retrieval
local_retriever = LocalEnvRetrieval()
local_vars = local_retriever.get_env_variables(['API_KEY', 'DEBUG_MODE'])

# AWS Secrets Manager retrieval
aws_retriever = AWSEnvRetrieval(
    secret_name='production-secrets',
    region='us-west-2',
    role_name='app-secrets-role'
)
aws_vars = aws_retriever.get_env_variables(['DB_PASSWORD', 'JWT_SECRET'])
```

### Using Factory Pattern

```python
from env_factory import EnvFactory

# Create retrievers using factory
local_retriever = EnvFactory.create_local_retriever()
aws_retriever = EnvFactory.create_aws_retriever(
    secret_name='my-secrets',
    region='eu-west-1', 
    role_name='secrets-access-role'
)

# Use the retrievers
config = local_retriever.get_env_variables(['PORT', 'HOST'])
secrets = aws_retriever.get_env_variables(['API_TOKEN', 'WEBHOOK_SECRET'])
```

## Configuration

### Environment Variables Setup (.env file)

**IMPORTANT**: You must store your environment variables in a `.env` file in your project root. This is mandatory for the package to work properly.

#### For Local Development
Create a `.env` file in your project root:

```bash
# .env file
API_KEY=your-local-api-key
DATABASE_URL=postgresql://localhost:5432/mydb
DEBUG=true
PORT=8000
```

#### For AWS Secrets Manager
**MANDATORY**: Your `.env` file must include the AWS Account ID:

```bash
# .env file - REQUIRED for AWS functionality
AWS_ACCOUNT_ID=123456789012
ENVIRONMENT=production
```

### Additional AWS Requirements

Before using AWS functionality, ensure you have:

1. **AWS Account ID in .env**: The `AWS_ACCOUNT_ID` must be set in your `.env` file (mandatory)
2. **AWS Credentials**: Configure AWS credentials (via AWS CLI, IAM roles, or environment variables)
3. **IAM Permissions**: Ensure your IAM role has access to the specified secrets

Required IAM permissions:
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "sts:AssumeRole"
            ],
            "Resource": "*"
        }
    ]
}
```

## Advanced Usage

### Error Handling

```python
from env_factory import get_env_variables, ConfigurationError, SecretRetrievalError

try:
    variables = get_env_variables(
        keys=['API_KEY', 'SECRET_TOKEN'],
        source='aws',
        secret_name='app-secrets',
        region='us-east-1',
        role_name='secrets-role'
    )
except ConfigurationError as e:
    print(f"Configuration error: {e}")
except SecretRetrievalError as e:
    print(f"Failed to retrieve secrets: {e}")
```

### Custom Implementation

```python
from env_factory import EnvRetrieval
from typing import Dict, List, Optional

class CustomEnvRetrieval(EnvRetrieval):
    """Custom environment retrieval implementation"""
    
    def __init__(self, config_file: str):
        self.config_file = config_file
    
    def get_env_variables(self, keys: List[str]) -> Dict[str, Optional[str]]:
        # Your custom implementation here
        pass
```

### Logging Configuration

```python
import logging

# Configure logging to see debug information
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('env_factory')

# Now you'll see debug logs when retrieving variables
variables = get_env_variables(['API_KEY'], source='local')
```

## API Reference

### Classes

#### `EnvRetrieval` (Abstract Base Class)
Base class for all environment retrieval implementations.

**Methods:**
- `get_env_variables(keys: List[str]) -> Dict[str, Optional[str]]`: Abstract method to retrieve environment variables

#### `LocalEnvRetrieval`
Retrieves environment variables from the local system environment.

**Methods:**
- `get_env_variables(keys: List[str]) -> Dict[str, Optional[str]]`: Get variables from local environment

#### `AWSEnvRetrieval`
Retrieves environment variables from AWS Secrets Manager.

**Constructor:**
- `__init__(secret_name: str, region: str, role_name: str)`

**Methods:**
- `get_env_variables(keys: List[str]) -> Dict[str, Optional[str]]`: Get variables from AWS Secrets Manager

#### `EnvFactory`
Factory class for creating environment retrieval instances.

**Static Methods:**
- `create_local_retriever() -> LocalEnvRetrieval`: Create local retriever
- `create_aws_retriever(secret_name: str, region: str, role_name: str) -> AWSEnvRetrieval`: Create AWS retriever

### Functions

#### `get_env_variables()`
Convenience function for retrieving environment variables.

**Parameters:**
- `keys: List[str]` - List of environment variable names to retrieve
- `source: str = "local"` - Environment source ('local' or 'aws')
- `**kwargs` - Additional arguments for AWS (secret_name, region, role_name)

**Returns:**
- `Dict[str, Optional[str]]` - Dictionary mapping keys to their values

### Exceptions

#### `EnvFactoryError`
Base exception for the env_factory package.

#### `ConfigurationError`
Raised when configuration parameters are invalid.

#### `SecretRetrievalError`
Raised when secret retrieval from AWS fails.

## Examples

### Web Application Configuration

```python
from env_factory import get_env_variables

# Load configuration for a web application
config = get_env_variables([
    'DATABASE_URL',
    'SECRET_KEY',
    'DEBUG',
    'PORT',
    'REDIS_URL'
])

# Handle missing variables
database_url = config.get('DATABASE_URL')
if not database_url:
    raise ValueError("DATABASE_URL is required")

debug_mode = config.get('DEBUG', 'false').lower() == 'true'
port = int(config.get('PORT', '8000'))
```

### Multi-Environment Setup

```python
import os
from env_factory import get_env_variables

# Determine environment
environment = os.getenv('ENVIRONMENT', 'development')

if environment == 'production':
    # Use AWS Secrets Manager in production
    config = get_env_variables(
        keys=['DATABASE_PASSWORD', 'API_KEY', 'JWT_SECRET'],
        source='aws',
        secret_name='prod-app-secrets',
        region='us-east-1',
        role_name='prod-secrets-role'
    )
else:
    # Use local environment variables in development
    config = get_env_variables([
        'DATABASE_PASSWORD',
        'API_KEY', 
        'JWT_SECRET'
    ])

print(f"Loaded {len([v for v in config.values() if v is not None])} variables")
```

### Batch Processing with Multiple Sources

```python
from env_factory import EnvFactory

# Create retrievers
local_retriever = EnvFactory.create_local_retriever()
aws_retriever = EnvFactory.create_aws_retriever(
    secret_name='shared-secrets',
    region='us-west-2',
    role_name='batch-processing-role'
)

# Get different types of configuration
app_config = local_retriever.get_env_variables([
    'LOG_LEVEL',
    'BATCH_SIZE', 
    'WORKER_COUNT'
])

sensitive_config = aws_retriever.get_env_variables([
    'DATABASE_PASSWORD',
    'API_TOKEN',
    'ENCRYPTION_KEY'
])

# Merge configurations
full_config = {**app_config, **sensitive_config}
```

## Best Practices

1. **Always use .env files**: Store all environment variables in a `.env` file in your project root (mandatory)
2. **Load .env at startup**: Use `python-dotenv` to load your `.env` file before using env_factory
3. **AWS Account ID**: Always include `AWS_ACCOUNT_ID` in your `.env` file when using AWS functionality
4. **Environment-Specific Sources**: Use local environment variables for development and AWS Secrets Manager for production
5. **Error Handling**: Always handle `ConfigurationError` and `SecretRetrievalError` exceptions
6. **Key Validation**: Check if required environment variables are present and not None
7. **Security**: Never commit your `.env` file to version control (add it to `.gitignore`)

### .gitignore Example
```bash
# .gitignore
.env
.env.local
.env.production
*.env
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Changelog

### v1.0.0
- Initial release
- Support for local environment variables
- Support for AWS Secrets Manager
- Factory pattern implementation
- Comprehensive error handling
- Type hints and documentation