Skip to main content

onion_config is a Python package that allows for easy configuration management. It allows for loading and validating configuration data from environment variables and config files in JSON and YAML formats.

Project description

onion_config

MIT License GitHub Workflow Status GitHub release (latest SemVer) PyPI PyPI - Python Version

onion_config is a Python package that allows for easy configuration management. It allows for loading and validating configuration data from environment variables and config files in JSON and YAML formats.

Pydantic based custom config 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 configs from YAML and JSON files
  • Update the default config with additional configurations (extra_configs 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)

Installation

1. Prerequisites

  • Python (>= v3.7)
  • PyPi (>= v21)

2. Install onion-config package

Choose one of the following methods to install the package [A ~ F]:

A. [RECOMMENDED] Install from PyPi

# Install or upgrade package:
pip install -U onion-config

B. Install latest version from GitHub

# Install package by git:
pip install git+https://github.com/bybatkhuu/mod.python-config.git

C. Install from pre-built release files

  1. Download .whl or .tar.gz file from releases - https://github.com/bybatkhuu/mod.python-config/releases
  2. Install with pip:
# Install from .whl file:
pip install ./onion_config-[VERSION]-py3-none-any.whl
# Or install from .tar.gz file:
pip install ./onion_config-[VERSION].tar.gz

D. Install from source code by building package

# Clone repository by git:
git clone https://github.com/bybatkhuu/mod.python-config.git onion_config
cd ./onion_config

# Install python build tool:
pip install -U pip build

# Build python package:
python -m build

_VERSION=$(./scripts/get-version.sh)

# Install from .whl file:
pip install ./dist/onion_config-${_VERSION}-py3-none-any.whl
# Or install from .tar.gz file:
pip install ./dist/onion_config-${_VERSION}.tar.gz

E. Install with pip editable development mode (from source code)

# Clone repository by git:
git clone https://github.com/bybatkhuu/mod.python-config.git onion_config
cd ./onion_config

# Install with editable development mode:
pip install -e .

F. Manually add to PYTHONPATH (not recommended)

# Clone repository by git:
git clone https://github.com/bybatkhuu/mod.python-config.git onion_config
cd ./onion_config

# Install python dependencies:
pip install -r ./requirements.txt

# Add current path to PYTHONPATH:
export PYTHONPATH="${PWD}:${PYTHONPATH}"

Usage/Examples

Simple

To use onion_config, import the ConfigLoader class from the package:

from onion_config import ConfigLoader

You can then create an instance of ConfigLoader:

config_loader = ConfigLoader(auto_load=True)

This will automatically load configuration data from environment variables and config files located in the default directory ('./configs'). The configuration data can then be accessed via the config property of the ConfigLoader instance:

config = config_loader.config

Advanced

configs/config.yml:

env: production

app:
  name: "My App"
  bind_host: "0.0.0.0"
  version: "0.0.1"
  ignore_val: "Ignore me"

logger:
  output: "stdout"

configs/logger.json:

{
    "logger":
    {
        "level": "info",
        "output": "file"
    }
}

.env:

ENV=development

DEBUG=true

APP_NAME="New App"
APP_SECRET="my_secret"

main.py:

import sys
import pprint
import logging
from enum import Enum
from typing import Union

from pydantic import Field, SecretStr
from pydantic_settings import SettingsConfigDict

from onion_config import ConfigLoader, BaseConfig


logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logger = logging.getLogger(__name__)


# 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

# Environments as Enum:
class EnvEnum(str, Enum):
    LOCAL = "local"
    DEVELOPMENT = "development"
    TEST = "test"
    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)

    model_config = SettingsConfigDict(extra="ignore", env_prefix="APP_")

# Main config schema:
class ConfigSchema(BaseConfig):
    env: EnvEnum = Field(EnvEnum.LOCAL)
    debug: bool = Field(False)
    app: AppConfig = Field(...)


if __name__ == "__main__":
    try:
        # Main 'config' object for usage:
        config: ConfigSchema = ConfigLoader(
            config_schema=ConfigSchema, pre_load_hook=_pre_load_hook
        ).load()
    except Exception:
        logger.exception("Failed to load config:")
        exit(2)

    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")
    logger.info(f" Config:\n{pprint.pformat(config.model_dump())}\n")

    try:
        # This will raise ValidationError
        config.app.port = 8443
    except Exception as e:
        logger.error(f" {e}\n")

Run the example:

cd ./examples

python ./main.py

Output:

INFO:__main__: ENV: development
INFO:__main__: DEBUG: True
INFO:__main__: Extra: Something extra!
INFO:__main__: Logger: {'level': 'info', 'output': 'stdout'}
INFO:__main__: App: name='New App' bind_host='0.0.0.0' port=80 secret=SecretStr('**********') version='0.0.1' description=None
INFO:__main__: Secret: 'my_secret'

INFO:__main__: Config:
{'app': {'bind_host': '0.0.0.0',
         'description': None,
         'name': 'New App',
         'port': 80,
         'secret': SecretStr('**********'),
         'version': '0.0.1'},
 'debug': True,
 'env': <EnvEnum.DEVELOPMENT: 'development'>,
 'extra_val': 'Something extra!',
 'logger': {'level': 'info', 'output': 'stdout'}}

ERROR:__main__: 1 validation error for AppConfig
port
  Instance is frozen [type=frozen_instance, input_value=8443, input_type=int]

Running Tests

To run tests, run the following command:

# Install python development dependencies:
pip install -r ./requirements.dev.txt

# Run tests:
python -m pytest -sv

FAQ

What is the order of loading config?

Load order:

  1. Load environment variables from .env file.
  2. Check required environment variables are exist or not.
  3. Load config files from configs_dir into config_data.
  4. Load extra config files from extra_configs_dir into config_data.
  5. Execute pre_load_hook method to modify config_data.
  6. Init config_schema with config_data into final config.

Environment Variables

You can use the following environment variables inside .env.example file:

ONION_CONFIG_EXTRA_DIR="./extra_configs_dir"

Documentation


References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

onion_config-3.0.0.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

onion_config-3.0.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

Details for the file onion_config-3.0.0.tar.gz.

File metadata

  • Download URL: onion_config-3.0.0.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.17

File hashes

Hashes for onion_config-3.0.0.tar.gz
Algorithm Hash digest
SHA256 0b36aa6abe95f443d2ae2907486590767ce8ba6838b46fdfc484ec743010874c
MD5 7131117476b79683b7dd2d7e545158e4
BLAKE2b-256 2b57cb4af10e5697698a418026c287d9995b4556fe4cfd6d507b897d69778dd5

See more details on using hashes here.

File details

Details for the file onion_config-3.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for onion_config-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 671d86b910f14a883178f7432acbf12e4b0e62f06a75c3086511c0361cd38737
MD5 27a23935aab63e1af7fc07a11be4d04e
BLAKE2b-256 b776881d20f54aa3a4f4e7bbebccd947e9d14c696f785dc4207f9516713f5125

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page