Skip to main content

ConfMe: Configuration Made Easy 💖

image image image image

ConfMe is a simple to use, production ready application configuration management library, which takes into consideration the following three thoughts:

  1. Access to configuration values must be safe at runtime. No myconfig['value1']['subvalue'] anymore!
  2. The configuration must be checked for consistency at startup e.g. type check, range check, ...
  3. Secrets shall be injectable from environment variables

ConfMe makes all these features possible with just a few type annotations on plain Python objects.

Installation

ConfMe can be installed from the official python package repository pypi

Using pip:

pip install confme

Using pipenv:

pipenv install confme

Using poetry:

poetry add confme

Using uv:

uv add confme

Basic Usage of confme

Define your config structure as plain python objects with type annotations:

from confme import BaseConfig

class DatabaseConfig(BaseConfig):
    host: str
    port: int
    user: str

class MyConfig(BaseConfig):
    name: str
    database: DatabaseConfig

Create a configuration yaml file with the same structure as your configuration classes have:

name: "Database Application"
database:
    host: "localhost"
    port: 5000
    user: "any-db-user"

Load the yaml file into your Python object structure and access it in a secure manner:

my_config = MyConfig.load('config.yaml')

print(f'Using database connection {my_config.database.host} '
      f'on port {my_config.database.port}')

In the background the yaml file is parsed and mapped to the defined object structure. While mapping the values to object properties, type checks are performed. If a value is not available or is not of the correct type, an error is generated already when the configuration is loaded.

Supported Annotations

ConfMe is based on pydantic and supports all annotations provided by pydantic. The most important annotations are listed and explain bellow. For the whole list, please checkout Field Types:

Secret

With the Secret annotation you can inject secrets from environment variables directly into your configuration structure. This is especially handy when you're deploying applications by using docker. Therefore, let's extend the previous example with a Secret annotation:

from confme import BaseConfig
from confme.annotation import Secret

class DatabaseConfig(BaseConfig):
    ...
    password: str = Secret('highSecurePassword')

Now set the password to the defined environment variable:

export highSecurePassword="This is my password"

Load your config and check for the injected password.

my_config = MyConfig.load('config.yaml')
print(f'My password is: {my_config.database.password}')

Range

ConfME supports OpenRange, ClosedRange and MixedRange values. The terms open and close are similar to open and closed intervals in mathematics. This means, if you want to include the lower and upper range use ClosedRange otherwise OpenRange:

  • ClosedRange(2, 3) will include 2 and 3
  • OpenRange(2, 3) will not include 2 and 3

If you want to have a mixture of both, e.g. include 2 but exclude 3 use MixedRange:

  • MixedRange(ge=2, lt=3) will include 2 but exclude 3
from confme import BaseConfig
from confme.annotation import ClosedRange

class DatabaseConfig(BaseConfig):
    ...
    password: int = ClosedRange(2, 3)

Enum

If a Python Enum is set as type annotation, ConfMe expect to find the enum value in the configuration file.

from confme import BaseConfig
from enum import Enum

class DatabaseConnection(Enum):
    TCP = 'tcp'
    UDP = 'udp'

class DatabaseConfig(BaseConfig):
    ...
    connection_type: DatabaseConnection

Path Interpolation with %(here)s

Similar to Alembic's INI file functionality, ConfMe supports path interpolation using the %(here)s placeholder. This allows you to specify paths relative to the location of the configuration file, making your configurations portable across different environments.

Usage

In your configuration file, use %(here)s to reference the directory containing the config file:

# config/app_config.yaml
script_location: "%(here)s/scripts"
data_dir: "%(here)s/../data"
log_file: "%(here)s/logs/app.log"

With a Python config class:

from confme import BaseConfig

class AppConfig(BaseConfig):
    script_location: str
    data_dir: str
    log_file: str

config = AppConfig.load('config/app_config.yaml')
print(config.script_location)  # Absolute path to config/scripts

How It Works

When the configuration file is loaded, %(here)s is automatically replaced with the absolute path to the directory containing the configuration file. This happens before the configuration is validated, so you get fully resolved paths in your configuration object.

Benefits

  • Portable configurations: Your config files work regardless of where the project is located
  • Relative paths made easy: No need to hardcode absolute paths or compute them at runtime
  • Works with nested configs: Path interpolation works at any level of your configuration hierarchy

Nested Configuration Example

name: "my_app"
paths:
  scripts: "%(here)s/scripts"
  data: "%(here)s/data"
  logs: "%(here)s/logs"
class PathsConfig(BaseConfig):
    scripts: str
    data: str
    logs: str

class AppConfig(BaseConfig):
    name: str
    paths: PathsConfig

config = AppConfig.load('config/app.yaml')
# All paths are now absolute paths relative to config/

Switching configuration based on Environment

A very common situation is that configurations must be changed based on the execution environment (dev, test, prod). This can be accomplished by registering a folder with one .yaml file per environment and seting the ENV environment variable to the value you need. An example could look like this:

Let's assume we have three environments (dev, test, prod) and one configuration file per environment in the following folder structure:

project
│
└───config
│   │   my_prod_config.yaml
│   │   my_test_config.yaml
│   │   my_dev_config.yaml
│   
└───src
│   │   app.py
│   │   my_config.py

The definition of my_config.py is equivalent to the one used in the basic introduction section and app.py uses our configuration the following way:

# we register the folder where ConfME can find the configuration files
MyConfig.register_folder(Path(__file__).parent / '../config')
...

# we access the instance of the corresponding configuration file anywhere in our project. 
my_config = MyConfig.get()
print(f'Using database connection {my_config.database.host} '
      f'on port {my_config.database.port}')

If now one of the following environment variables (precedence in descending order): ['env', 'environment', 'environ', 'stage'] is set e.g. export ENV=prod it will load the configuration file with prod in its name.

Parameter overwrite

In addition to loading configuration parameters from the configuration file, they can be passed/overwritten from the command line or environment variables. Thereby, the following precedences apply (lower number means higher precedence):

  1. Command Line Arguments: Check if parameter is set as command line argument. If not go one line done...
  2. Environment Variables: Check if parameter is set as environment variable. If not go one line done...
  3. Configuration File: If value was not found in one of the previous sources, it will check in the configuration file.

Overwrite Parameters from Command Line

Especially in the Data Science and Machine Learning area it is useful to pass certain parameters for experimental purposes as command line arguments. Therefore, all properties defined in the configuration classes are automatically offered as command line arguments in the following format:

my_program.py:

from confme import BaseConfig

class DatabaseConfig(BaseConfig):
    host: str
    port: int
    user: str

class MyConfig(BaseConfig):
    name: int
    database: DatabaseConfig

config = MyConfig.load('test.yaml')

When you now start your program from the command line with the ++help argument, you get the full list of all configuration options. CAVEAT! In order to not interfere with other cli tools, the prefix - was changed to +:

$ python my_program.py --help
usage: my_program.py [+h] [++name NAME] [++database.host DATABASE.HOST] [++database.port DATABASE.PORT] [++database.user DATABASE.USER]

optional arguments:
  +h, ++help            show this help message and exit

Configuration Parameters:
  With the parameters specified bellow, the configuration values from the config file can be overwritten.

  ++name NAME
  ++database.host DATABASE.HOST
  ++database.port DATABASE.PORT
  ++database.user DATABASE.USER

Overwrite Parameters with Environment Variables

Likewise to overwriting parameters from the commandline you can also overwrite by passing environment variables. Therefore, simply set the environment variable in the same format as it would be passed as command line arguments and run your application:

$ export database.host=localhost
$ python my_programm.py

Breaking Changes in v2

Pydantic is the underlying library powering ConfMe and with the update to pydantic v2 some breaking changes where introduced. However, we tried our best to minimize the impact on your project and only passed a selection of changes to you. Please find these documented bellow:

Required, optional and nullable fields

Pydantic V2 changes some of the logic for specifying whether a field annotated as Optional is required (i.e., has no default value) or not (i.e., has a default value of None or any other value of the corresponding type), and now more closely matches the behavior of dataclasses. Similarly, fields annotated as Any no longer have a default value of None.

The following table describes the behavior of field annotations in V2:

State Field Definition
Required, cannot be None f1: str
Not required, cannot be None, is 'abc' by default f3: str = 'abc'
Required, can be None f2: Optional[str]
Not required, can be None, is None by default f3: Optional[str] = None
Not required, can be None, is 'abc' by default f3: Optional[str] = 'abc'
Not required, cannot be None f4: str = 'Foobar'
Required, can be any type (including None) f5: Any
Not required, can be any type (including None) f6: Any = None

LICENSE

ConfMe is released under the MIT license.

Metadata

Release files for confme 2.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for confme 2.2.0
File Size Uploaded
confme-2.2.0.tar.gz 11.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for confme 2.2.0
File Interpreter ABI Platform
confme-2.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.3 kB

Release files / confme-2.2.0.tar.gz

Download URL confme-2.2.0.tar.gz
Size 11.1 kB
Tags Source
SHA-256 checksum
How to use checksums
51150f4f5f1bc63ac76aaa45cecf53e96a9e4fd40327455592903b7ac19c4006
BLAKE2b-256 checksum
How to use checksums
1ecaf9ed9fcf1a4de8a6bcfd0ba01b42d16141d4d88928802d25ab2789f11714
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / confme-2.2.0-py3-none-any.whl

Download URL confme-2.2.0-py3-none-any.whl
Size 15.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87aaeb0b343012a56059e916aecbfdf5d8fd277bc32836a375d661881f1a9fd7
BLAKE2b-256 checksum
How to use checksums
3e7a12273810affe25fa5cd547e3f92bc596f4291bda362d77c2be2f31446b69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

2.2.0 This release

2 release files

2.1.0

2 release files

2.0.4

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.1

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.4

2 release files

1.0.2

2 release files

1.0.1

2 release files

0.1.0

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page