initial commit
This commit is contained in:
commit
38d8880b7b
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.idea/
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
include README.md
|
148
README.md
Normal file
148
README.md
Normal 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
5
configurator/__init__.py
Normal 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
36
configurator/core.py
Normal 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)
|
3
configurator/exceptions.py
Normal file
3
configurator/exceptions.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class ConfigurationError(Exception):
|
||||||
|
"""Custom Exception for configuration errors."""
|
||||||
|
pass
|
0
configurator/utils.py
Normal file
0
configurator/utils.py
Normal file
8
configurator/validators.py
Normal file
8
configurator/validators.py
Normal 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
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
python-decouple
|
26
setup.py
Normal file
26
setup.py
Normal 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,
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user