Code Monkey home page Code Monkey logo

confz's Introduction

ConfZ – Pydantic Config Management

test documentation coverage python pypi

ConfZ is a configuration management library for Python based on pydantic. It easily allows you to

  • load your configuration from config files, environment variables, command line arguments and more
  • transform the loaded data into a desired format and validate it
  • access the results as Python dataclass-like objects with full IDE support

It furthermore supports you in common use cases like:

  • Multiple environments
  • Singleton with lazy loading
  • Config changes for unit tests
  • Custom config sources

UPDATE: ConfZ 2 is here, with support for pydantic 2 and improved naming conventions. Check out the migration guide.

πŸ“¦ Installation

ConfZ is on PyPI and can be installed with pip:

pip install confz

πŸš€ Quick Start

The first step of using ConfZ is to declare your config classes and sources, for example in config.py:

from confz import BaseConfig, FileSource
from pydantic import SecretStr, AnyUrl

class DBConfig(BaseConfig):
    user: str
    password: SecretStr

class APIConfig(BaseConfig):
    host: AnyUrl
    port: int
    db: DBConfig

    CONFIG_SOURCES = FileSource(file='/path/to/config.yml')

Thanks to pydantic, you can use a wide variety of field types and validators.

From now on, in any other file, you can access your config directly:

from config import APIConfig

print(f"Serving API at {APIConfig().host}, port {APIConfig().port}.")

As can be seen, the config does neither have to be loaded explicitly, nor instantiated globally. ConfZ automatically loads your config as defined in CONFIG_SOURCES the first time you access it. Thanks to its singleton mechanism, this happens the first time only, afterwards you get back a cached, immutable instance, behaving like any other pydantic instance.

assert APIConfig() is APIConfig()   # true because of singleton mechanism
APIConfig().port = 1234             # raises an error because of immutability
APIConfig().model_dump()            # call pydantic's method to get a dict representation

Note: While the implicit and hidden loading of your config might be surprising and feel a bit like Python magic at first, it allows you to reduce a lot of boilerplate. Instead of having to load your config explicitly and then passing it down to all code layers that need it, you can directly access it from anywhere by just importing your config class and accessing for example APIConfig().db.user directly.

More Config Sources

ConfZ is highly flexible in defining the source of your config. Do you have multiple environments? No Problem:

from confz import BaseConfig, FileSource

class MyConfig(BaseConfig):
    ...
    CONFIG_SOURCES = FileSource(
        folder='/path/to/config/folder',
        file_from_env='ENVIRONMENT'
    )

Your config file can now be defined in the environment variable ENVIRONMENT and is relative to folder.

You can also provide a list as config source and read for example from environment variables including a .env file and from command line arguments:

from confz import BaseConfig, EnvSource, CLArgSource

class MyConfig(BaseConfig):
    ...
    CONFIG_SOURCES = [
        EnvSource(allow_all=True, file=".env.local"),
        CLArgSource(prefix='conf_')
    ]

ConfZ now tries to populate your config either from environment variables having the same name as your attributes or by reading command line arguments that start with conf_. Recursive models are supported too, for example if you want to control the user-name in the API above, you can either set the environment variable DB.USER or pass the command line argument --conf_db.user.

Explicit Loading

In some scenarios, the config should not be a global singleton, but loaded explicitly and passed around locally. Instead of defining CONFIG_SOURCES as class variable, the sources can also be defined in the constructor directly:

from confz import BaseConfig, FileSource, EnvSource

class MyConfig(BaseConfig):
    number: int
    text: str

config1 = MyConfig(config_sources=FileSource(file='/path/to/config.yml'))
config2 = MyConfig(config_sources=EnvSource(prefix='CONF_', allow=['text']), number=1)
config3 = MyConfig(number=1, text='hello world')

As can be seen, additional keyword-arguments can be provided as well.

Note: If neither class variable CONFIG_SOURCES nor constructor argument config_sources is provided, BaseConfig behaves like a regular pydantic class.

Change Config Values

In some scenarios, you might want to change your config values, for example within a unit test. However, if you set the CONFIG_SOURCES class variable, this is not directly possible. To overcome this, every config class provides a context manager to temporarily change your config:

from confz import BaseConfig, FileSource, DataSource

class MyConfig(BaseConfig):
    number: int
    CONFIG_SOURCES = FileSource(file="/path/to/config.yml")

print(MyConfig().number)                            # will print the value from the config-file

new_source = DataSource(data={'number': 42})
with MyConfig.change_config_sources(new_source):
    print(MyConfig().number)                        # will print '42'

print(MyConfig().number)                            # will print the value from the config-file again

Early Validation

By default, your config gets loaded the first time you instantiate the class, e.g. with MyConfig().attribute. This prevents side effects like loading a file while you import your config classes. If the config class cannot populate all mandatory fields in the correct format, pydantic will raise an error at this point. To make sure this does not happen in an inconvenient moment, you can also instruct ConfZ to load all configs beforehand:

from confz import validate_all_configs

if __name__ == '__main__':
    validate_all_configs()
    # your application code

The function validate_all_configs will instantiate all config classes defined in your code at any (reachable) location that have CONFIG_SOURCES set.

πŸ“– Documentation

Now you've seen the two ways how ConfZ can be used: With class variable config sources, unlocking a singleton with lazy loading, or with keyword argument config sources, allowing to directly load your config values. In both cases, defining your config sources from files, command line arguments and environment variables is highly flexible (and also extendable, by the way), while pydantic still makes sure that everything matches your expectations in the end. You've also seen how to temporarily change your config for example in unit tests and how to validate your singleton config classes early in the code already.

The full documentation of ConfZ's features can be found at readthedocs.

ℹ️ About

ConfZ was programmed and will be maintained by ZΓΌhlke. The first version was realized by Silvan. Special thanks to Iwan with his ConfMe, which inspired this project.

Want to contribute to ConfZ? Check out the contribution instruction & guidelines.

confz's People

Contributors

ajanon avatar alex-dr avatar andreww85 avatar bbsbb avatar deepaerial avatar dependabot[bot] avatar jpoppe avatar mopeze avatar silvanmelchior avatar silvanmelchior-zuehlke avatar thunderkey avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.