# glean-config

A flexible, thread-safe Python configuration manager using TOML files with support for environment variables, base64 encoding, and fileless operation modes.

## Features

- **TOML-based configuration**: Easy-to-read and write configuration 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

## 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'
```

## 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()
```

## TOML File Structure

```toml
config_name = 'my_application'

[environment]
# Regex pattern for environment variables to auto-load
env_rex = '^AP_'
# 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
```

## API Reference

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

Get or create the singleton config instance.

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

### 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
pytest tests/

# Run with coverage
pytest tests/ --cov=glean_config

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

## 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).

## License

MIT

## Contributing

Contributions are welcome! Please ensure:

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