initial commit

This commit is contained in:
agatha 2024-06-14 13:54:40 -04:00
commit 38d8880b7b
10 changed files with 232 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea/
venv/
__pycache__/
*.py[cod]

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
include README.md

148
README.md Normal file
View File

@ -0,0 +1,148 @@
# Configurator
`Configurator` is a Python package designed for efficient and standardized configuration management in your
applications. It uses a singleton pattern to ensure that configuration is loaded once and accessible throughout your
application. It supports merging custom configurations, validation of required configurations, and ease of access
through getter methods.
## Features
- Singleton pattern for configuration management
- Support for loading and merging custom configurations
- Simple validation for required configuration variables
- Easily extendable for additional configuration sources
## Installation
You can install the package via pip:
```bash
pip install configurator
```
Or by cloning this repository and installing with:
```bash
git clone https://github.com/yourusername/configurator.git
cd configurator
pip install .
```
## Usage
### Initial Setup
To use `Configurator` in your application, you first need to load the configuration. This should typically be done once
at the start of your application, for instance, in the main entry point or the initial setup script.
```python
from configurator import load_config, get_config, ConfigurationError
# Define custom configuration for the application
custom_config = {
'DATABASE_URL': 'sqlite:///default.db',
'SECRET_KEY': 'defaultsecretkey',
}
def main():
try:
# Load configuration with custom settings
load_config(custom_config=custom_config)
# Example usage of get_config
database_url = get_config('DATABASE_URL')
print(f"Database URL: {database_url}")
# Example usage of another config variable
secret_key = get_config('SECRET_KEY')
print(f"Secret Key: {secret_key}")
except ConfigurationError as e:
print(f"Failed to load configuration: {e}")
exit(1)
if __name__ == "__main__":
main()
```
### Accessing Configuration Values
You can access configuration values using the `get_config` function. If the key does not exist, you can provide a
default value.
```python
from configurator import get_config
log_level = get_config('LOG_LEVEL', 'INFO')
print(f"Log Level: {log_level}")
debug_mode = get_config('DEBUG', False)
print(f"Debug Mode: {debug_mode}")
```
### Configuration Validation
The package includes basic validation to ensure that all required configurations are provided. If a required variable is
missing, a `ConfigurationError` will be raised.
You can customize validation requirements in the `validators` module if needed.
## Custom Configuration
Custom configurations can be passed to the `load_config` function. These will be merged with the default configurations
provided in the environment or settings files.
Example:
```python
custom_config = {
'API_ENDPOINT': 'https://api.example.com',
'TIMEOUT': 30,
}
load_config(custom_config)
api_endpoint = get_config('API_ENDPOINT')
print(f"API Endpoint: {api_endpoint}")
```
## Extending Configurator
You can extend the `Configurator` to support additional configuration sources like JSON files, remote configuration
services, etc. This would typically involve modifying the `load_config` function.
```python
def load_config(custom_config=None, json_file=None) -> dict:
global _config_values
if _config_values is None:
default_config = {
'LOG_LEVEL': config('LOG_LEVEL', default='INFO'),
'DEBUG': config('DEBUG', default='false', cast=bool),
'ENV': config('ENV', default='DEV')
}
if json_file:
with open(json_file, 'r') as jf:
json_config = json.load(jf)
default_config.update(json_config)
# Merge custom config if present
if custom_config:
default_config.update(custom_config)
config_values = {key: config(key, default=value) for key, value in default_config.items()}
required_vars = [key for key, value in default_config.items() if value is None]
validate_config(config_values, required_vars)
_config_values = config_values
return _config_values
```
## Contributing
Contributions are welcome! Please fork the repository and submit a pull request with your changes. For major changes,
please open an issue first to discuss what you would like to change.
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

5
configurator/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from .core import load_config, get_config
from .validators import validate_config
from .exceptions import ConfigurationError
__all__ = ['load_config', 'get_config', 'validate_config', 'ConfigurationError']

36
configurator/core.py Normal file
View File

@ -0,0 +1,36 @@
from decouple import config
from .validators import validate_config
# Global state to hold loaded configuration (Singleton pattern)
_config_values = None
def load_config(custom_config=None) -> dict:
"""Load all configuration variables and validate them."""
global _config_values
if _config_values is None:
default_config = {
'LOG_LEVEL': config('LOG_LEVEL', default='INFO'),
'DEBUG': config('DEBUG', default='false', cast=bool),
'ENV': config('ENV', default='DEV')
}
# Merge custom configurator if present
if custom_config:
default_config.update(custom_config)
config_values = {key: config(key, default=value) for key, value in default_config.items()}
required_vars = [key for key, value in default_config.items() if value is None]
validate_config(config_values, required_vars)
_config_values = config_values
return _config_values
def get_config(key: str, default=None):
"""Get a configuration value by key."""
config_values = load_config()
return config_values.get(key, default)

View File

@ -0,0 +1,3 @@
class ConfigurationError(Exception):
"""Custom Exception for configuration errors."""
pass

0
configurator/utils.py Normal file
View File

View File

@ -0,0 +1,8 @@
from .exceptions import ConfigurationError
def validate_config(config_values, required_vars):
"""Validate the presence and format of required configuration variables."""
for key in required_vars:
if key not in config_values or config_values[key] is None:
raise ConfigurationError(f'Missing required configuration: {key}')

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
python-decouple

26
setup.py Normal file
View File

@ -0,0 +1,26 @@
from setuptools import setup, find_packages
with open("README.md", "r") as fh:
long_description = fh.read()
with open("requirements.txt", "r") as fh:
required = fh.read().splitlines()
setup(
name="configurator",
version="0.1.0",
author="agathanonymous",
author_email="agatha@juggalol.com",
description="A configuration management package",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://git.juggalol.com/agatha/configurator",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
install_requires=required,
)