# Onion Config (Python Config)
[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bybatkhuu/module.python-config/2.build-publish.yml?logo=GitHub)](https://github.com/bybatkhuu/module.python-config/actions/workflows/2.build-publish.yml)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bybatkhuu/module.python-config?logo=GitHub&color=blue)](https://github.com/bybatkhuu/module.python-config/releases)
[![PyPI](https://img.shields.io/pypi/v/onion-config?logo=PyPi)](https://pypi.org/project/onion-config)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/onion-config?logo=Python)](https://docs.conda.io/en/latest/miniconda.html)
`onion_config` is a Python package designed for easy configuration management. It supports loading and validating configuration data from environment variables and configuration files in JSON and YAML formats. It is a `Pydantic` based custom configuration package for Python projects.
## โจ Features
- **Main config** based on **Pydantic schema** - <https://pypi.org/project/pydantic>
- Load **environment variables** - <https://pypi.org/project/python-dotenv>
- Load from **multiple** configs directories
- Load configs from **YAML** and **JSON** files
- Update the default config with additional configurations (**`extra_dir`** directory)
- **Pre-load hook** function to modify config data before loading and validation
- **Validate config** values with **Pydantic validators**
- Config as **dictionary** or **Pydantic model** (with type hints)
- **Pre-defined** base config schema for common config (**`BaseConfig`**)
- **Base** for custom config loader (**`ConfigLoader`**)
- Support **Pydantic-v1** and **Pydantic-v2**
---
## ๐ Installation
### 1. ๐ง Prerequisites
- Install **Python (>= v3.9)** and **pip (>= 23)**:
- **[RECOMMENDED] [Miniconda (v3)](https://docs.anaconda.com/miniconda)**
- *[arm64/aarch64] [Miniforge (v3)](https://github.com/conda-forge/miniforge)*
- *[Python virutal environment] [venv](https://docs.python.org/3/library/venv.html)*
[OPTIONAL] For **DEVELOPMENT** environment:
- Install [**git**](https://git-scm.com/downloads)
- Setup an [**SSH key**](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) ([video tutorial](https://www.youtube.com/watch?v=snCP3c7wXw0))
### 2. ๐ฅ Download or clone the repository
> [!TIP]
> Skip this step, if you're going to install the package directly from **PyPi** or **GitHub** repository.
**2.1.** Prepare projects directory (if not exists):
```sh
# Create projects directory:
mkdir -pv ~/workspaces/projects
# Enter into projects directory:
cd ~/workspaces/projects
```
**2.2.** Follow one of the below options **[A]**, **[B]** or **[C]**:
**OPTION A.** Clone the repository:
```sh
git clone https://github.com/bybatkhuu/module.python-config.git && \
cd module.python-config
```
**OPTION B.** Clone the repository (for **DEVELOPMENT**: git + ssh key):
```sh
git clone git@github.com:bybatkhuu/module.python-config.git && \
cd module.python-config
```
**OPTION C.** Download source code:
1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module.python-config/releases).
2. Extract it into the projects directory.
### 3. ๐ฆ Install the package
> [!NOTE]
> Choose one of the following methods to install the package **[A ~ E]**:
**OPTION A.** [**RECOMMENDED**] Install from **PyPi**:
> [!WARNING]
> If you wanted to use **Pydantic-v1**, but if you already installed `pydantic-settings` and `pydantic-core`, remove it before installing **Pydantic-v1**:
```sh
pip uninstall -y pydantic-settings
pip uninstall -y pydantic-core
# Then install with Pydantic-v1:
pip install -U onion-config[pydantic-v1]
```
> [!WARNING]
> If you wanted to use **Pydantic-v2**, but if you already installed `onion-config` package just by \
> *`pip install -U onion-config`* command, and this will not install `pydantic-settings`. \
> For this case, '**`env_prefix`**' **WILL NOT WORK** for `BaseConfig` or `BaseSettings` without `pydantic-settings`! This is Pydantic-v2's problem, and there could be some other problems. \
> So fix these issues re-install `onion-config` with `pydantic-settings`:
```sh
# Install with pydantic-settings for Pydantic-v2:
pip install -U onion-config[pydantic-settings]
```
**OPTION B.** Install latest version directly from **GitHub** repository:
```sh
# Pydantic-v1:
pip install git+https://github.com/bybatkhuu/module.python-config.git[pydantic-v1]
# Pydantic-v2:
pip install git+https://github.com/bybatkhuu/module.python-config.git[pydantic-settings]
```
**OPTION C.** Install from the downloaded **source code**:
```sh
# Install directly from the source code:
# Pydantic-v1:
pip install .[pydantic-v1]
# Pydantic-v2:
pip install .[pydantic-settings]
# Or install with editable mode (for DEVELOPMENT):
# Pydantic-v1:
pip install -e .[pydantic-v1]
# Pydantic-v2:
pip install -e .[pydantic-settings]
```
**OPTION D.** Install from **pre-built release** files:
1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module.python-config/releases)
2. Install with pip:
```sh
# Pydantic-v1:
# Install from .whl file:
pip install ./onion_config-[VERSION]-py3-none-any.whl[pydantic-v1]
# Or install from .tar.gz file:
pip install ./onion_config-[VERSION].tar.gz[pydantic-v1]
# Pydantic-v2:
# Install from .whl file:
pip install ./onion_config-[VERSION]-py3-none-any.whl[pydantic-settings]
# Or install from .tar.gz file:
pip install ./onion_config-[VERSION].tar.gz[pydantic-settings]
```
**OPTION E.** Copy the **module** into the project directory (for **testing**):
```sh
# Install python dependencies:
pip install -r ./requirements/requirements.core.txt
# Pydantic-v1:
pip install -r ./requirements/requirements.pydantic-v1.txt
# Pydantic-v2:
pip install -r ./requirements/requirements.pydantic-settings.txt
# Copy the module source code into the project:
cp -r ./src/onion_config [PROJECT_DIR]
# For example:
cp -r ./src/onion_config /some/path/project/
```
## ๐ธ Usage/Examples
### Simple
[**`examples/simple/.env`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/.env)
```sh
ENV=production
```
[**`examples/simple/configs/1.base.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/configs/1.base.yml):
```yaml
env: test
app:
name: "My App"
version: "0.0.1"
nested:
key: "value"
```
[**`examples/simple/configs/2.extra.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/configs/2.extra.yml):
```yaml
app:
name: "New App"
nested:
some: "value"
description: "Description of my app."
another_val:
extra: 1
```
[**`examples/simple/main.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/main.py)
```python
import pprint
from loguru import logger
try:
import pydantic_settings
_has_pydantic_settings = True
except ImportError:
_has_pydantic_settings = False
from onion_config import ConfigLoader, BaseConfig
class ConfigSchema(BaseConfig):
env: str = "local"
try:
config: ConfigSchema = ConfigLoader(config_schema=ConfigSchema).load()
except Exception:
logger.exception("Failed to load config:")
exit(2)
if __name__ == "__main__":
logger.info(f"All: {config}")
logger.info(f"App name: {config.app['name']}")
if _has_pydantic_settings:
# Pydantic-v2:
logger.info(f"Config:\n{pprint.pformat(config.model_dump())}\n")
else:
# Pydantic-v1:
logger.info(f"Config:\n{pprint.pformat(config.dict())}\n")
```
Run the [**`examples/simple`**](https://github.com/bybatkhuu/module.python-config/tree/main/examples/simple):
```sh
cd ./examples/simple
python ./main.py
```
**Output**:
```txt
2023-09-01 00:00:00.000 | INFO | __main__:<module>:29 - All: env='production' another_val={'extra': 1} app={'name': 'New App', 'version': '0.0.1', 'nested': {'key': 'value', 'some': 'value'}, 'description': 'Description of my app.'}
2023-09-01 00:00:00.000 | INFO | __main__:<module>:30 - App name: New App
2023-09-01 00:00:00.000 | INFO | __main__:<module>:35 - Config:
{'another_val': {'extra': 1},
'app': {'description': 'Description of my app.',
'name': 'New App',
'nested': {'key': 'value', 'some': 'value'},
'version': '0.0.1'},
'env': 'production'}
```
### Advanced
[**`examples/advanced/.env.base`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/.env.base):
```sh
ENV=development
DEBUG=true
APP_NAME="Old App"
ONION_CONFIG_EXTRA_DIR="extra_configs"
```
[**`examples/advanced/.env.prod`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/.env.prod):
```sh
ENV=production
APP_NAME="New App"
APP_SECRET="my_secret"
```
[**`examples/advanced/configs/config.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs/config.yml):
```yaml
env: local
app:
name: "My App"
port: 9000
bind_host: "0.0.0.0"
version: "0.0.1"
ignore_val: "Ignore me"
logger:
output: "file"
```
[**`examples/advanced/configs/logger.json`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs/logger.json):
```json
{
"logger": {
"level": "info",
"output": "stdout"
}
}
```
[**`examples/advanced/configs_2/config.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs_2/config.yml):
```yaml
extra:
config:
key1: 1
```
[**`examples/advanced/configs_2/config_2.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs_2/config_2.yml):
```yaml
extra:
config:
key2: 2
```
[**`examples/advanced/extra_configs/extra.json`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/extra_configs/extra.json):
```json
{
"extra": {
"type": "json"
}
}
```
[**`examples/advanced/schema.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/schema.py):
```python
from enum import Enum
from typing import Union
import pydantic
from pydantic import Field, SecretStr
_has_pydantic_settings = False
if "2.0.0" <= pydantic.__version__:
try:
from pydantic_settings import SettingsConfigDict
_has_pydantic_settings = True
except ImportError:
pass
from onion_config import BaseConfig
# Environments as Enum:
class EnvEnum(str, Enum):
LOCAL = "local"
DEVELOPMENT = "development"
TEST = "test"
DEMO = "demo"
STAGING = "staging"
PRODUCTION = "production"
# App config schema:
class AppConfig(BaseConfig):
name: str = Field("App", min_length=2, max_length=32)
bind_host: str = Field("localhost", min_length=2, max_length=128)
port: int = Field(8000, ge=80, lt=65536)
secret: SecretStr = Field(..., min_length=8, max_length=64)
version: str = Field(..., min_length=5, max_length=16)
description: Union[str, None] = Field(None, min_length=4, max_length=64)
if _has_pydantic_settings:
# Pydantic-v2:
model_config = SettingsConfigDict(extra="ignore", env_prefix="APP_")
else:
# Pydantic-v1:
class Config:
extra = "ignore"
env_prefix = "APP_"
# Main config schema:
class ConfigSchema(BaseConfig):
env: EnvEnum = Field(EnvEnum.LOCAL)
debug: bool = Field(False)
app: AppConfig = Field(...)
```
[**`examples/advanced/config.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/config.py):
```python
from loguru import logger
from onion_config import ConfigLoader
from schema import ConfigSchema
# Pre-load function to modify config data before loading and validation:
def _pre_load_hook(config_data: dict) -> dict:
config_data["app"]["port"] = "80"
config_data["extra_val"] = "Something extra!"
return config_data
config = None
try:
_config_loader = ConfigLoader(
config_schema=ConfigSchema,
configs_dirs=["configs", "configs_2", "/not_exists/path/configs_3"],
env_file_paths=[".env", ".env.base", ".env.prod"],
pre_load_hook=_pre_load_hook,
config_data={"base": "start_value"},
warn_mode="ALWAYS",
)
# Main config object:
config: ConfigSchema = _config_loader.load()
except Exception:
logger.exception("Failed to load config:")
exit(2)
```
[**`examples/advanced/main.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/main.py):
```python
import pprint
from loguru import logger
try:
import pydantic_settings
_has_pydantic_settings = True
except ImportError:
_has_pydantic_settings = False
from config import config
if __name__ == "__main__":
logger.info(f"All: {config}")
logger.info(f"ENV: {config.env}")
logger.info(f"DEBUG: {config.debug}")
logger.info(f"Extra: {config.extra_val}")
logger.info(f"Logger: {config.logger}")
logger.info(f"App: {config.app}")
logger.info(f"Secret: '{config.app.secret.get_secret_value()}'\n")
if _has_pydantic_settings:
# Pydantic-v2:
logger.info(f"Config:\n{pprint.pformat(config.model_dump())}\n")
else:
# Pydantic-v1:
logger.info(f"Config:\n{pprint.pformat(config.dict())}\n")
try:
# This will raise ValidationError
config.app.port = 8443
except Exception as e:
logger.error(f"{e}\n")
```
Run the [**`examples/advanced`**](https://github.com/bybatkhuu/module.python-config/tree/main/examples/advanced):
```sh
cd ./examples/advanced
python ./main.py
```
**Output**:
```txt
2023-09-01 00:00:00.000 | INFO | onion_config._base:load:143 - Loading all configs...
2023-09-01 00:00:00.000 | WARNING | onion_config._base:_load_dotenv_file:201 - '/home/user/workspaces/projects/onion_config/examples/advanced/.env' file is not exist!
2023-09-01 00:00:00.000 | WARNING | onion_config._base:_load_configs_dir:257 - '/not_exists/path/configs_3' directory is not exist!
2023-09-01 00:00:00.000 | SUCCESS | onion_config._base:load:171 - Successfully loaded all configs!
2023-09-01 00:00:00.000 | INFO | __main__:<module>:19 - All: env=<EnvEnum.PRODUCTION: 'production'> debug=True app=AppConfig(name='New App', bind_host='0.0.0.0', port=80, secret=SecretStr('**********'), version='0.0.1', description=None) extra={'config': {'key1': 1, 'key2': 2}, 'type': 'json'} extra_val='Something extra!' logger={'output': 'stdout', 'level': 'info'} base='start_value'
2023-09-01 00:00:00.000 | INFO | __main__:<module>:20 - ENV: production
2023-09-01 00:00:00.000 | INFO | __main__:<module>:21 - DEBUG: True
2023-09-01 00:00:00.000 | INFO | __main__:<module>:22 - Extra: Something extra!
2023-09-01 00:00:00.000 | INFO | __main__:<module>:23 - Logger: {'output': 'stdout', 'level': 'info'}
2023-09-01 00:00:00.000 | INFO | __main__:<module>:24 - App: name='New App' bind_host='0.0.0.0' port=80 secret=SecretStr('**********') version='0.0.1' description=None
2023-09-01 00:00:00.000 | INFO | __main__:<module>:25 - Secret: 'my_secret'
2023-09-01 00:00:00.000 | INFO | __main__:<module>:30 - Config:
{'app': {'bind_host': '0.0.0.0',
'description': None,
'name': 'New App',
'port': 80,
'secret': SecretStr('**********'),
'version': '0.0.1'},
'base': 'start_value',
'debug': True,
'env': <EnvEnum.PRODUCTION: 'production'>,
'extra': {'config': {'key1': 1, 'key2': 2}, 'type': 'json'},
'extra_val': 'Something extra!',
'logger': {'level': 'info', 'output': 'stdout'}}
2023-09-01 00:00:00.000 | ERROR | __main__:<module>:36 - "AppConfig" is immutable and does not support item assignment
```
๐
---
### ๐ Environment Variables
[**`.env.example`**](https://github.com/bybatkhuu/module.python-config/blob/main/.env.example):
```sh
# ENV=development
# DEBUG=true
ONION_CONFIG_EXTRA_DIR="./extra_configs"
```
---
## ๐งช Running Tests
To run tests, run the following command:
```sh
# Install core dependencies:
pip install -r ./requirements/requirements.core.txt
# Pydantic-v1:
pip install -r ./requirements/requirements.pydantic-v1.txt
# Pydantic-v2:
pip install -r ./requirements/requirements.pydantic-settings.txt
# Install python test dependencies:
pip install -r ./requirements.test.txt
# Run tests:
python -m pytest -sv -o log_cli=true
# Or use the test script:
./scripts/test.sh -l -v -c
```
## ๐๏ธ Build Package
To build the python package, run the following command:
```sh
# Install python build dependencies:
pip install -r ./requirements/requirements.build.txt
# Build python package:
python -m build
# Or use the build script:
./scripts/build.sh
```
## ๐ Generate Docs
To build the documentation, run the following command:
```sh
# Install python documentation dependencies:
pip install -r ./requirements/requirements.docs.txt
# Serve documentation locally (for development):
mkdocs serve
# Or use the docs script:
./scripts/docs.sh
# Or build documentation:
mkdocs build
# Or use the docs script:
./scripts/docs.sh -b
```
## ๐ Documentation
- [Docs](https://github.com/bybatkhuu/module.python-config/blob/main/docs)
- [Home](https://github.com/bybatkhuu/module.python-config/blob/main/docs/README.md)
### Getting Started
- [Prerequisites](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/prerequisites.md)
- [Installation](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/installation.md)
- [Configuration](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/configuration.md)
- [Examples](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/examples.md)
- [Error Codes](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/error-codes.md)
### [API Documentation](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/api-docs/README.md)
### Development
- [Test](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/test.md)
- [Build](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/build.md)
- [Docs](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/docs.md)
- [CI/CD](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/cicd.md)
- [Scripts](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/scripts/README.md)
- [File Structure](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/file-structure.md)
- [Sitemap](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/sitemap.md)
- [Contributing](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/contributing.md)
- [Roadmap](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/roadmap.md)
### [Release Notes](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/release-notes.md)
### About
- [FAQ](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/faq.md)
- [Authors](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/authors.md)
- [Contact](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/contact.md)
- [License](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/license.md)
---
## ๐ References
- <https://docs.pydantic.dev>
- <https://github.com/pydantic/pydantic>
- <https://docs.pydantic.dev/latest/usage/pydantic_settings>
- <https://github.com/pydantic/pydantic-settings>
- <https://saurabh-kumar.com/python-dotenv>
- <https://github.com/theskumar/python-dotenv>
- <https://packaging.python.org/tutorials/packaging-projects>
Raw data
{
"_id": null,
"home_page": null,
"name": "onion-config",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "onion_config, config, configs, dotenv, python-dotenv, pydantic, pydantic-config, pydantic-settings, custom-config",
"author": null,
"author_email": "Batkhuu Byambajav <batkhuu10@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/a4/35/8941d940646faa1162887826d7e063492ba11f3643f253f6179b9057575e/onion_config-5.1.1.tar.gz",
"platform": null,
"description": "# Onion Config (Python Config)\n\n[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bybatkhuu/module.python-config/2.build-publish.yml?logo=GitHub)](https://github.com/bybatkhuu/module.python-config/actions/workflows/2.build-publish.yml)\n[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bybatkhuu/module.python-config?logo=GitHub&color=blue)](https://github.com/bybatkhuu/module.python-config/releases)\n[![PyPI](https://img.shields.io/pypi/v/onion-config?logo=PyPi)](https://pypi.org/project/onion-config)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/onion-config?logo=Python)](https://docs.conda.io/en/latest/miniconda.html)\n\n`onion_config` is a Python package designed for easy configuration management. It supports loading and validating configuration data from environment variables and configuration files in JSON and YAML formats. It is a `Pydantic` based custom configuration package for Python projects.\n\n## \u2728 Features\n\n- **Main config** based on **Pydantic schema** - <https://pypi.org/project/pydantic>\n- Load **environment variables** - <https://pypi.org/project/python-dotenv>\n- Load from **multiple** configs directories\n- Load configs from **YAML** and **JSON** files\n- Update the default config with additional configurations (**`extra_dir`** directory)\n- **Pre-load hook** function to modify config data before loading and validation\n- **Validate config** values with **Pydantic validators**\n- Config as **dictionary** or **Pydantic model** (with type hints)\n- **Pre-defined** base config schema for common config (**`BaseConfig`**)\n- **Base** for custom config loader (**`ConfigLoader`**)\n- Support **Pydantic-v1** and **Pydantic-v2**\n\n---\n\n## \ud83d\udee0 Installation\n\n### 1. \ud83d\udea7 Prerequisites\n\n- Install **Python (>= v3.9)** and **pip (>= 23)**:\n - **[RECOMMENDED] [Miniconda (v3)](https://docs.anaconda.com/miniconda)**\n - *[arm64/aarch64] [Miniforge (v3)](https://github.com/conda-forge/miniforge)*\n - *[Python virutal environment] [venv](https://docs.python.org/3/library/venv.html)*\n\n[OPTIONAL] For **DEVELOPMENT** environment:\n\n- Install [**git**](https://git-scm.com/downloads)\n- Setup an [**SSH key**](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) ([video tutorial](https://www.youtube.com/watch?v=snCP3c7wXw0))\n\n### 2. \ud83d\udce5 Download or clone the repository\n\n> [!TIP]\n> Skip this step, if you're going to install the package directly from **PyPi** or **GitHub** repository.\n\n**2.1.** Prepare projects directory (if not exists):\n\n```sh\n# Create projects directory:\nmkdir -pv ~/workspaces/projects\n\n# Enter into projects directory:\ncd ~/workspaces/projects\n```\n\n**2.2.** Follow one of the below options **[A]**, **[B]** or **[C]**:\n\n**OPTION A.** Clone the repository:\n\n```sh\ngit clone https://github.com/bybatkhuu/module.python-config.git && \\\n cd module.python-config\n```\n\n**OPTION B.** Clone the repository (for **DEVELOPMENT**: git + ssh key):\n\n```sh\ngit clone git@github.com:bybatkhuu/module.python-config.git && \\\n cd module.python-config\n```\n\n**OPTION C.** Download source code:\n\n1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module.python-config/releases).\n2. Extract it into the projects directory.\n\n### 3. \ud83d\udce6 Install the package\n\n> [!NOTE]\n> Choose one of the following methods to install the package **[A ~ E]**:\n\n**OPTION A.** [**RECOMMENDED**] Install from **PyPi**:\n\n> [!WARNING]\n> If you wanted to use **Pydantic-v1**, but if you already installed `pydantic-settings` and `pydantic-core`, remove it before installing **Pydantic-v1**:\n\n```sh\npip uninstall -y pydantic-settings\npip uninstall -y pydantic-core\n\n# Then install with Pydantic-v1:\npip install -U onion-config[pydantic-v1]\n```\n\n> [!WARNING]\n> If you wanted to use **Pydantic-v2**, but if you already installed `onion-config` package just by \\\n> *`pip install -U onion-config`* command, and this will not install `pydantic-settings`. \\\n> For this case, '**`env_prefix`**' **WILL NOT WORK** for `BaseConfig` or `BaseSettings` without `pydantic-settings`! This is Pydantic-v2's problem, and there could be some other problems. \\\n> So fix these issues re-install `onion-config` with `pydantic-settings`:\n\n```sh\n# Install with pydantic-settings for Pydantic-v2:\npip install -U onion-config[pydantic-settings]\n```\n\n**OPTION B.** Install latest version directly from **GitHub** repository:\n\n```sh\n# Pydantic-v1:\npip install git+https://github.com/bybatkhuu/module.python-config.git[pydantic-v1]\n\n# Pydantic-v2:\npip install git+https://github.com/bybatkhuu/module.python-config.git[pydantic-settings]\n```\n\n**OPTION C.** Install from the downloaded **source code**:\n\n```sh\n# Install directly from the source code:\n# Pydantic-v1:\npip install .[pydantic-v1]\n# Pydantic-v2:\npip install .[pydantic-settings]\n\n# Or install with editable mode (for DEVELOPMENT):\n# Pydantic-v1:\npip install -e .[pydantic-v1]\n# Pydantic-v2:\npip install -e .[pydantic-settings]\n```\n\n**OPTION D.** Install from **pre-built release** files:\n\n1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module.python-config/releases)\n2. Install with pip:\n\n```sh\n# Pydantic-v1:\n# Install from .whl file:\npip install ./onion_config-[VERSION]-py3-none-any.whl[pydantic-v1]\n# Or install from .tar.gz file:\npip install ./onion_config-[VERSION].tar.gz[pydantic-v1]\n\n# Pydantic-v2:\n# Install from .whl file:\npip install ./onion_config-[VERSION]-py3-none-any.whl[pydantic-settings]\n# Or install from .tar.gz file:\npip install ./onion_config-[VERSION].tar.gz[pydantic-settings]\n```\n\n**OPTION E.** Copy the **module** into the project directory (for **testing**):\n\n```sh\n# Install python dependencies:\npip install -r ./requirements/requirements.core.txt\n\n# Pydantic-v1:\npip install -r ./requirements/requirements.pydantic-v1.txt\n# Pydantic-v2:\npip install -r ./requirements/requirements.pydantic-settings.txt\n\n# Copy the module source code into the project:\ncp -r ./src/onion_config [PROJECT_DIR]\n# For example:\ncp -r ./src/onion_config /some/path/project/\n```\n\n## \ud83d\udeb8 Usage/Examples\n\n### Simple\n\n[**`examples/simple/.env`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/.env)\n\n```sh\nENV=production\n```\n\n[**`examples/simple/configs/1.base.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/configs/1.base.yml):\n\n```yaml\nenv: test\n\napp:\n name: \"My App\"\n version: \"0.0.1\"\n nested:\n key: \"value\"\n```\n\n[**`examples/simple/configs/2.extra.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/configs/2.extra.yml):\n\n```yaml\napp:\n name: \"New App\"\n nested:\n some: \"value\"\n description: \"Description of my app.\"\n\nanother_val:\n extra: 1\n```\n\n[**`examples/simple/main.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/simple/main.py)\n\n```python\nimport pprint\n\nfrom loguru import logger\ntry:\n import pydantic_settings\n\n _has_pydantic_settings = True\nexcept ImportError:\n _has_pydantic_settings = False\n\nfrom onion_config import ConfigLoader, BaseConfig\n\n\nclass ConfigSchema(BaseConfig):\n env: str = \"local\"\n\ntry:\n config: ConfigSchema = ConfigLoader(config_schema=ConfigSchema).load()\nexcept Exception:\n logger.exception(\"Failed to load config:\")\n exit(2)\n\nif __name__ == \"__main__\":\n logger.info(f\"All: {config}\")\n logger.info(f\"App name: {config.app['name']}\")\n\n if _has_pydantic_settings:\n # Pydantic-v2:\n logger.info(f\"Config:\\n{pprint.pformat(config.model_dump())}\\n\")\n else:\n # Pydantic-v1:\n logger.info(f\"Config:\\n{pprint.pformat(config.dict())}\\n\")\n```\n\nRun the [**`examples/simple`**](https://github.com/bybatkhuu/module.python-config/tree/main/examples/simple):\n\n```sh\ncd ./examples/simple\n\npython ./main.py\n```\n\n**Output**:\n\n```txt\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:29 - All: env='production' another_val={'extra': 1} app={'name': 'New App', 'version': '0.0.1', 'nested': {'key': 'value', 'some': 'value'}, 'description': 'Description of my app.'}\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:30 - App name: New App\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:35 - Config:\n{'another_val': {'extra': 1},\n 'app': {'description': 'Description of my app.',\n 'name': 'New App',\n 'nested': {'key': 'value', 'some': 'value'},\n 'version': '0.0.1'},\n 'env': 'production'}\n```\n\n### Advanced\n\n[**`examples/advanced/.env.base`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/.env.base):\n\n```sh\nENV=development\nDEBUG=true\nAPP_NAME=\"Old App\"\nONION_CONFIG_EXTRA_DIR=\"extra_configs\"\n```\n\n[**`examples/advanced/.env.prod`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/.env.prod):\n\n```sh\nENV=production\nAPP_NAME=\"New App\"\nAPP_SECRET=\"my_secret\"\n```\n\n[**`examples/advanced/configs/config.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs/config.yml):\n\n```yaml\nenv: local\n\napp:\n name: \"My App\"\n port: 9000\n bind_host: \"0.0.0.0\"\n version: \"0.0.1\"\n ignore_val: \"Ignore me\"\n\nlogger:\n output: \"file\"\n```\n\n[**`examples/advanced/configs/logger.json`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs/logger.json):\n\n```json\n{\n \"logger\": {\n \"level\": \"info\",\n \"output\": \"stdout\"\n }\n}\n```\n\n[**`examples/advanced/configs_2/config.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs_2/config.yml):\n\n```yaml\nextra:\n config:\n key1: 1\n```\n\n[**`examples/advanced/configs_2/config_2.yml`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/configs_2/config_2.yml):\n\n```yaml\nextra:\n config:\n key2: 2\n```\n\n[**`examples/advanced/extra_configs/extra.json`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/extra_configs/extra.json):\n\n```json\n{\n \"extra\": {\n \"type\": \"json\"\n }\n}\n```\n\n[**`examples/advanced/schema.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/schema.py):\n\n```python\nfrom enum import Enum\nfrom typing import Union\n\nimport pydantic\nfrom pydantic import Field, SecretStr\n_has_pydantic_settings = False\nif \"2.0.0\" <= pydantic.__version__:\n try:\n from pydantic_settings import SettingsConfigDict\n\n _has_pydantic_settings = True\n except ImportError:\n pass\n\nfrom onion_config import BaseConfig\n\n\n# Environments as Enum:\nclass EnvEnum(str, Enum):\n LOCAL = \"local\"\n DEVELOPMENT = \"development\"\n TEST = \"test\"\n DEMO = \"demo\"\n STAGING = \"staging\"\n PRODUCTION = \"production\"\n\n# App config schema:\nclass AppConfig(BaseConfig):\n name: str = Field(\"App\", min_length=2, max_length=32)\n bind_host: str = Field(\"localhost\", min_length=2, max_length=128)\n port: int = Field(8000, ge=80, lt=65536)\n secret: SecretStr = Field(..., min_length=8, max_length=64)\n version: str = Field(..., min_length=5, max_length=16)\n description: Union[str, None] = Field(None, min_length=4, max_length=64)\n\n if _has_pydantic_settings:\n # Pydantic-v2:\n model_config = SettingsConfigDict(extra=\"ignore\", env_prefix=\"APP_\")\n else:\n # Pydantic-v1:\n class Config:\n extra = \"ignore\"\n env_prefix = \"APP_\"\n\n# Main config schema:\nclass ConfigSchema(BaseConfig):\n env: EnvEnum = Field(EnvEnum.LOCAL)\n debug: bool = Field(False)\n app: AppConfig = Field(...)\n```\n\n[**`examples/advanced/config.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/config.py):\n\n```python\nfrom loguru import logger\n\nfrom onion_config import ConfigLoader\n\nfrom schema import ConfigSchema\n\n\n# Pre-load function to modify config data before loading and validation:\ndef _pre_load_hook(config_data: dict) -> dict:\n config_data[\"app\"][\"port\"] = \"80\"\n config_data[\"extra_val\"] = \"Something extra!\"\n return config_data\n\nconfig = None\ntry:\n _config_loader = ConfigLoader(\n config_schema=ConfigSchema,\n configs_dirs=[\"configs\", \"configs_2\", \"/not_exists/path/configs_3\"],\n env_file_paths=[\".env\", \".env.base\", \".env.prod\"],\n pre_load_hook=_pre_load_hook,\n config_data={\"base\": \"start_value\"},\n warn_mode=\"ALWAYS\",\n )\n # Main config object:\n config: ConfigSchema = _config_loader.load()\nexcept Exception:\n logger.exception(\"Failed to load config:\")\n exit(2)\n\n```\n\n[**`examples/advanced/main.py`**](https://github.com/bybatkhuu/module.python-config/blob/main/examples/advanced/main.py):\n\n```python\nimport pprint\n\nfrom loguru import logger\ntry:\n import pydantic_settings\n _has_pydantic_settings = True\nexcept ImportError:\n _has_pydantic_settings = False\n\nfrom config import config\n\n\nif __name__ == \"__main__\":\n logger.info(f\"All: {config}\")\n logger.info(f\"ENV: {config.env}\")\n logger.info(f\"DEBUG: {config.debug}\")\n logger.info(f\"Extra: {config.extra_val}\")\n logger.info(f\"Logger: {config.logger}\")\n logger.info(f\"App: {config.app}\")\n logger.info(f\"Secret: '{config.app.secret.get_secret_value()}'\\n\")\n\n if _has_pydantic_settings:\n # Pydantic-v2:\n logger.info(f\"Config:\\n{pprint.pformat(config.model_dump())}\\n\")\n else:\n # Pydantic-v1:\n logger.info(f\"Config:\\n{pprint.pformat(config.dict())}\\n\")\n\n try:\n # This will raise ValidationError\n config.app.port = 8443\n except Exception as e:\n logger.error(f\"{e}\\n\")\n```\n\nRun the [**`examples/advanced`**](https://github.com/bybatkhuu/module.python-config/tree/main/examples/advanced):\n\n```sh\ncd ./examples/advanced\n\npython ./main.py\n```\n\n**Output**:\n\n```txt\n2023-09-01 00:00:00.000 | INFO | onion_config._base:load:143 - Loading all configs...\n2023-09-01 00:00:00.000 | WARNING | onion_config._base:_load_dotenv_file:201 - '/home/user/workspaces/projects/onion_config/examples/advanced/.env' file is not exist!\n2023-09-01 00:00:00.000 | WARNING | onion_config._base:_load_configs_dir:257 - '/not_exists/path/configs_3' directory is not exist!\n2023-09-01 00:00:00.000 | SUCCESS | onion_config._base:load:171 - Successfully loaded all configs!\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:19 - All: env=<EnvEnum.PRODUCTION: 'production'> debug=True app=AppConfig(name='New App', bind_host='0.0.0.0', port=80, secret=SecretStr('**********'), version='0.0.1', description=None) extra={'config': {'key1': 1, 'key2': 2}, 'type': 'json'} extra_val='Something extra!' logger={'output': 'stdout', 'level': 'info'} base='start_value'\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:20 - ENV: production\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:21 - DEBUG: True\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:22 - Extra: Something extra!\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:23 - Logger: {'output': 'stdout', 'level': 'info'}\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:24 - App: name='New App' bind_host='0.0.0.0' port=80 secret=SecretStr('**********') version='0.0.1' description=None\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:25 - Secret: 'my_secret'\n\n2023-09-01 00:00:00.000 | INFO | __main__:<module>:30 - Config:\n{'app': {'bind_host': '0.0.0.0',\n 'description': None,\n 'name': 'New App',\n 'port': 80,\n 'secret': SecretStr('**********'),\n 'version': '0.0.1'},\n 'base': 'start_value',\n 'debug': True,\n 'env': <EnvEnum.PRODUCTION: 'production'>,\n 'extra': {'config': {'key1': 1, 'key2': 2}, 'type': 'json'},\n 'extra_val': 'Something extra!',\n 'logger': {'level': 'info', 'output': 'stdout'}}\n\n2023-09-01 00:00:00.000 | ERROR | __main__:<module>:36 - \"AppConfig\" is immutable and does not support item assignment\n```\n\n\ud83d\udc4d\n\n---\n\n### \ud83c\udf0e Environment Variables\n\n[**`.env.example`**](https://github.com/bybatkhuu/module.python-config/blob/main/.env.example):\n\n```sh\n# ENV=development\n# DEBUG=true\n\nONION_CONFIG_EXTRA_DIR=\"./extra_configs\"\n```\n\n---\n\n## \ud83e\uddea Running Tests\n\nTo run tests, run the following command:\n\n```sh\n# Install core dependencies:\npip install -r ./requirements/requirements.core.txt\n\n# Pydantic-v1:\npip install -r ./requirements/requirements.pydantic-v1.txt\n# Pydantic-v2:\npip install -r ./requirements/requirements.pydantic-settings.txt\n\n# Install python test dependencies:\npip install -r ./requirements.test.txt\n\n# Run tests:\npython -m pytest -sv -o log_cli=true\n# Or use the test script:\n./scripts/test.sh -l -v -c\n```\n\n## \ud83c\udfd7\ufe0f Build Package\n\nTo build the python package, run the following command:\n\n```sh\n# Install python build dependencies:\npip install -r ./requirements/requirements.build.txt\n\n# Build python package:\npython -m build\n# Or use the build script:\n./scripts/build.sh\n```\n\n## \ud83d\udcdd Generate Docs\n\nTo build the documentation, run the following command:\n\n```sh\n# Install python documentation dependencies:\npip install -r ./requirements/requirements.docs.txt\n\n# Serve documentation locally (for development):\nmkdocs serve\n# Or use the docs script:\n./scripts/docs.sh\n\n# Or build documentation:\nmkdocs build\n# Or use the docs script:\n./scripts/docs.sh -b\n```\n\n## \ud83d\udcda Documentation\n\n- [Docs](https://github.com/bybatkhuu/module.python-config/blob/main/docs)\n- [Home](https://github.com/bybatkhuu/module.python-config/blob/main/docs/README.md)\n\n### Getting Started\n\n- [Prerequisites](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/prerequisites.md)\n- [Installation](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/installation.md)\n- [Configuration](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/configuration.md)\n- [Examples](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/examples.md)\n- [Error Codes](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/getting-started/error-codes.md)\n\n### [API Documentation](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/api-docs/README.md)\n\n### Development\n\n- [Test](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/test.md)\n- [Build](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/build.md)\n- [Docs](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/docs.md)\n- [CI/CD](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/cicd.md)\n- [Scripts](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/scripts/README.md)\n- [File Structure](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/file-structure.md)\n- [Sitemap](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/sitemap.md)\n- [Contributing](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/contributing.md)\n- [Roadmap](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/dev/roadmap.md)\n\n### [Release Notes](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/release-notes.md)\n\n### About\n\n- [FAQ](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/faq.md)\n- [Authors](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/authors.md)\n- [Contact](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/contact.md)\n- [License](https://github.com/bybatkhuu/module.python-config/blob/main/docs/pages/about/license.md)\n\n---\n\n## \ud83d\udcd1 References\n\n- <https://docs.pydantic.dev>\n- <https://github.com/pydantic/pydantic>\n- <https://docs.pydantic.dev/latest/usage/pydantic_settings>\n- <https://github.com/pydantic/pydantic-settings>\n- <https://saurabh-kumar.com/python-dotenv>\n- <https://github.com/theskumar/python-dotenv>\n- <https://packaging.python.org/tutorials/packaging-projects>\n",
"bugtrack_url": null,
"license": null,
"summary": "`onion_config` is a Python package designed for easy configuration management. It supports loading and validating configuration data from environment variables and configuration files in JSON and YAML formats. It is a `Pydantic` based custom configuration package for Python projects.",
"version": "5.1.1",
"project_urls": {
"Changelog": "https://github.com/bybatkhuu/module.python-config/CHANGELOG.md",
"Documentation": "https://bybatkhuu.github.io/module.python-config",
"Homepage": "https://github.com/bybatkhuu/module.python-config",
"Issues": "https://github.com/bybatkhuu/module.python-config/issues",
"Repository": "https://github.com/bybatkhuu/module.python-config.git"
},
"split_keywords": [
"onion_config",
" config",
" configs",
" dotenv",
" python-dotenv",
" pydantic",
" pydantic-config",
" pydantic-settings",
" custom-config"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b2098e8220539489c0361289d427bee890051d70887afaa004742c004de8f47b",
"md5": "cc8b56ae4daa2d52dd9ff92cdc1498c8",
"sha256": "af8f7cf42fbc52440c389f5cedb40190f67201de436b71eed4645c9313443533"
},
"downloads": -1,
"filename": "onion_config-5.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "cc8b56ae4daa2d52dd9ff92cdc1498c8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 14319,
"upload_time": "2025-01-19T02:57:19",
"upload_time_iso_8601": "2025-01-19T02:57:19.031936Z",
"url": "https://files.pythonhosted.org/packages/b2/09/8e8220539489c0361289d427bee890051d70887afaa004742c004de8f47b/onion_config-5.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a4358941d940646faa1162887826d7e063492ba11f3643f253f6179b9057575e",
"md5": "02a90cb24944123db1009df8151418d4",
"sha256": "cf9296643a1b612ff596dd13fb38c36ffe20b2a3a7c0c5b40fda066919f26b6a"
},
"downloads": -1,
"filename": "onion_config-5.1.1.tar.gz",
"has_sig": false,
"md5_digest": "02a90cb24944123db1009df8151418d4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 19686,
"upload_time": "2025-01-19T02:57:21",
"upload_time_iso_8601": "2025-01-19T02:57:21.410004Z",
"url": "https://files.pythonhosted.org/packages/a4/35/8941d940646faa1162887826d7e063492ba11f3643f253f6179b9057575e/onion_config-5.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-19 02:57:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "bybatkhuu",
"github_project": "module.python-config",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "onion-config"
}