glean-config 0.2.2
Add your description here
Requires Python: >=3.13

# glean-config

A flexible, thread-safe Python configuration manager supporting both TOML files and Kubernetes ConfigMaps, with environment variable integration, base64 encoding, and fileless operation modes.

## Features

- **TOML and ConfigMap support**: Load configuration from TOML files or Kubernetes ConfigMap YAML
- **Automatic format detection**: Seamlessly detects and parses TOML or ConfigMap files
- **Singleton pattern**: Ensures consistent configuration across your application
- **Thread-safe**: Built-in locking for concurrent access
- **Environment variable integration**: Auto-load environment variables by name or regex pattern
- **Automatic base64 encoding**: Prefix keys with `encoded_` for automatic encoding/decoding
- **Fileless mode**: Run without a config file for testing or ephemeral configurations
- **Context manager support**: Use with `with` statement for automatic saving
- **Kubernetes-ready**: Native support for ConfigMap YAML files

## Installation

```bash
pip install glean-config
```

Or for development:

```bash
git clone <repository-url>
cd glean-config
pip install -e .
```

## Quick Start

### Basic Usage

```python
from glean_config.config import Glean_config

# Load or create a config file
config = Glean_config.get_instance('config.toml')

# Set values
config['api_key'] = 'your-api-key'
config['debug'] = 'true'

# Get values
api_key = config['api_key']

# Save changes
config.save()
```

### Using the CLI

```bash
# Create/view config with a specific file
python -m glean_config -f config.toml

# Add a key-value pair
python -m glean_config -f config.toml -a "database_url::postgresql://localhost/mydb"

# Show a specific key
python -m glean_config -f config.toml -k database_url

# Use environment variable for config file location
export GLEAN_CONFIG_FILE=/path/to/config.toml
python -m glean_config -k database_url

# Run in fileless mode (no file or env var needed)
python -m glean_config -a "temp_key::temp_value"
```

## Configuration Modes

### 1. File-based Mode (Default)

```python
config = Glean_config.get_instance('myconfig.toml')
```

Loads configuration from the specified TOML file. Creates the file if it doesn't exist.

### 2. Environment Variable Mode

```python
# Set the environment variable
import os
os.environ['GLEAN_CONFIG_FILE'] = '/path/to/config.toml'

# No file argument needed
config = Glean_config.get_instance()
```

### 3. Fileless Mode

```python
# No file, no environment variable
config = Glean_config.get_instance()
# Config exists only in memory
config['key'] = 'value'
```

### 4. Kubernetes ConfigMap Mode

```python
# Load from ConfigMap YAML file
config = Glean_config.get_instance('configmap.yaml')

# Access values just like TOML
db_host = config['database_host']
config['new_setting'] = 'value'
```

#### ConfigMap Format

ConfigMaps must follow the standard Kubernetes structure:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-config
  namespace: default
data:
  database_host: localhost
  database_port: "5432"
  api_url: https://api.example.com
  # Base64 encoded secrets work too
  encoded_api_key: c2VjcmV0a2V5MTIz
```

See `example-configmap.yaml` for a complete example.

## Advanced Features

### Automatic Base64 Encoding

Keys starting with `encoded_` are automatically base64 encoded when set and decoded when retrieved:

```python
config['encoded_password'] = 'my-secret-password'
# Stored as base64 in the file

password = config['encoded_password']
# Returns: 'my-secret-password' (decoded)
```

### Environment Variable Integration

Configure your TOML file to automatically load environment variables:

```toml
config_name = 'myapp'

[environment]
# Regex pattern to match environment variables
env_rex = '^MYAPP_'
# Explicit list of environment variables to load
env = ['DATABASE_URL', 'SECRET_KEY']

[config_items]
# Your config items here
```

Now environment variables matching the pattern or in the list are automatically loaded:

```python
import os
os.environ['MYAPP_DEBUG'] = 'true'
os.environ['DATABASE_URL'] = 'postgresql://localhost/db'

config = Glean_config.get_instance('config.toml')
print(config['MYAPP_DEBUG'])  # 'true'
print(config['DATABASE_URL'])  # 'postgresql://localhost/db'
```

### Context Manager

```python
with Glean_config.get_instance('config.toml') as config:
    config['key'] = 'value'
    config['another_key'] = 'another_value'
# Automatically saved on exit
```

### Bulk Configuration

```python
# Replace entire config
new_config = {
    'api_key': 'new-key',
    'timeout': '30',
    'retries': '3'
}
config.set_config(new_config)

# Merge with existing config
config.set_config(new_config, merge=True)
```

### Export Formats

```python
# Export as TOML string
toml_str = config.get_toml()

# Export as JSON string
json_str = config.get_json()
```

## File Structure

### TOML Format

```toml
config_name = 'my_application'

[environment]
# Regex pattern for environment variables to auto-load
env_rex = '^APP_'
# Explicit environment variables to load
env = ['DATABASE_URL', 'SECRET_KEY']

[config_items]
# Your application configuration
api_key = "your-api-key"
debug = "false"
timeout = "30"
encoded_password = "bXktc2VjcmV0LXBhc3N3b3Jk"  # base64 encoded
```

### ConfigMap Format

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-config
  namespace: default
data:
  api_key: your-api-key
  debug: "false"
  timeout: "30"
  # Base64 encoded values work with encoded_ prefix
  encoded_password: bXktc2VjcmV0LXBhc3N3b3Jk
  # Multiline values are supported
  app_config: |
    log_level: debug
    max_connections: 100
```

## API Reference

### `Glean_config.get_instance(toml_file=None)`

Get or create the singleton config instance.

- **toml_file** (str, optional): Path to TOML or ConfigMap YAML file. If `None`, uses `glean_config_file` env var or fileless mode.
- **Returns**: `Glean_config` instance

**Note**: The library automatically detects whether the file is TOML or a Kubernetes ConfigMap YAML based on the `kind: ConfigMap` field.

### Instance Methods

#### `config[key]` / `config[key] = value`

Get or set configuration values.

#### `save(force=False)`

Save configuration to file (no-op in fileless mode).

- **force** (bool): Save even if not modified

#### `get_config()`

Get the entire config_items dictionary.

- **Returns**: dict

#### `set_config(config, merge=False)`

Set the entire config_items dictionary.

- **config** (dict): New configuration
- **merge** (bool): Merge with existing config instead of replacing

#### `get_toml()`

Export configuration as TOML string.

- **Returns**: str

#### `get_json()`

Export configuration as JSON string.

- **Returns**: str

#### `parameters()`

Get list of top-level configuration keys.

- **Returns**: list[str]

#### `close()`

Explicitly save and close the configuration.

## Testing

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=glean_config

# Run specific test file
uv run pytest tests/test_config.py -v
uv run pytest tests/test_configmap.py -v

# Run specific test
uv run pytest tests/test_config.py::TestGleanConfig::test_fileless_mode_no_args
```

### Test Coverage

The project includes comprehensive tests:
- **21 tests** for TOML configuration
- **10 tests** for ConfigMap support
- **12 tests** for CLI functionality

All 43 tests pass with good coverage of core functionality.

## Thread Safety

All operations are thread-safe using internal locks:

- Object-level lock for singleton instance creation and config updates
- File-level lock for concurrent file I/O operations

## Error Handling

### `ConfigObjectError`

Raised when trying to instantiate `Glean_config` directly instead of using `get_instance()`.

```python
from glean_config.config import Glean_config, ConfigObjectError

try:
    config = Glean_config('config.toml')  # Wrong!
except ConfigObjectError:
    config = Glean_config.get_instance('config.toml')  # Correct
```

### `KeyError`

Raised when configuration structure is invalid (missing `config_items` key).

## Additional Documentation

- **ConfigMap Support**: See [CONFIGMAP_SUPPORT.md](CONFIGMAP_SUPPORT.md) for detailed ConfigMap documentation
- **Example ConfigMap**: See [example-configmap.yaml](example-configmap.yaml) for a complete example

## Dependencies

- `tomli-w>=1.2.0` - TOML writing support
- `pyyaml>=6.0` - YAML/ConfigMap parsing

## License

MIT

## Contributing

Contributions are welcome! Please ensure:

1. All tests pass: `uv run pytest`
2. Code follows PEP 8 style guidelines
3. New features include tests
4. Documentation is updated