Skip to content

Introduction

DriConfig mimics Pydantic's Settings Management functionality, working with YAML configurations instead of environment variables or .env files.

The YAML language📎

YAML is a human-readable data-serialization language, commonly used for configuration files. It natively encodes scalars (such as strings, integers, and floats), lists, and dictionaries.

# config.yaml

parameter_a: "some string"
parameter_b: 1
parameter_c: 1.2
parameter_d: ["I'm", "a", "list"]

The above code block contains a sample configuration file written in the YAML language.

Info

If you want to run the rest of the code blocks in this document, place the contents of the previous one into a file called config.yaml in your working directory.

Tip

A YAML configuration file should always be a dictionary at its first level. The elements of this dictionary, then, could be of any type (scalars, lists or dictionaries).

The goal of DriConfig is to provide an interface between your Python code and such YAML confguration files.

The DriConfig class📎

DriConfig provides a base configuration class called DriConfig. This base class should be sub-classed in order to generate custom configuration classes that represent YAML configuration files.

from driconfig import DriConfig

class AppConfig(DriConfig):  # Inherits the base DriConfig class.
    """Empty configuration class."""

    pass

app_config = AppConfig()

print(app_config.model_dump_json())
"""
{}
"""

The DriConfig's DriConfigConfigDict dictionary📎

Now, we would want to read and parse the config.yaml file we created before. We need to configure our AppConfig class in order to point at that file.

from driconfig import DriConfig, DriConfigConfigDict

class AppConfig(DriConfig):
    """Empty configuration class."""

    """Configure AppConfig to point at the config.yaml file."""
    model_config = DriConfigConfigDict(
        config_folder=".",
        config_file_name="config.yaml",
    )

app_config = AppConfig()

print(app_config.model_dump_json())
"""
{}
"""

Warning

In Pydantic V2, to specify config on a model, you should set a class attribute called model_config to be a dict with the key/value pairs you want to be used as the config. The Pydantic V1 behavior to create a class called Config in the namespace of the parent BaseModel subclass is now deprecated.

Note

We have extended the use of Pydantic ConfigDict dictionary to host the YAML file information. In the ConfigDict section we detail which configurations have been added or modified.

Parsing YAML configurations📎

Now we would want to parse the configurations we put on our config.yaml file.

from typing import List

from driconfig import DriConfig, DriConfigConfigDict

class AppConfig(DriConfig):
    """Configuration class to parse the config.yaml file contents."""

    """Configure AppConfig to point at the config.yaml file."""
    model_config = DriConfigConfigDict(
        config_folder=".",
        config_file_name="config.yaml",
    )

    parameter_a: str
    parameter_b: int
    parameter_c: float
    parameter_d: List[str]

app_config = AppConfig()

print(app_config.model_dump_json(indent=4))
"""
{
    "parameter_a": "some string",
    "parameter_b": 1,
    "parameter_c": 1.2,
    "parameter_d": [
        "I'm",
        "a",
        "list"
    ]
}
"""

Note how we declared the configuration variable types following Pydantic's syntax. In fact, while parsing the config.yaml file DriConfig is performing type validation, so that if your configuration variable value is of an undesired type it will raise a validation error.

from driconfig import DriConfig, DriConfigConfigDict
from pydantic import ValidationError

class AppConfig(DriConfig):
    """Configuration class to parse the config.yaml file contents."""

    """Configure AppConfig to point at the config.yaml file."""
    model_config = DriConfigConfigDict(
        config_folder=".",
        config_file_name="config.yaml",
    )

    parameter_a: int

try:
    app_config = AppConfig()
except ValidationError as e:
    print(e)
"""
1 validation error for AppConfig
parameter_a
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='some string', input_type=str]
    For further information visit https://errors.pydantic.dev/2.2/v/int_parsing
"""

As you can guess, all the benefits from Pydantic are hold by the DriConfig base class. Then, apart from raising errors on incorrect types, values are casted to the correct type when possible, and even nested configurations can be expressed as nested Pydantic models.

In particular, also the default values behavior is preserved from Pydantic. This is, we could define a default value for a given configuration, so that if the configuration is not present in the YAML configuration file, it gets the default value from the configuration class definition.

from typing import List

from driconfig import DriConfig, DriConfigConfigDict

class AppConfig(DriConfig):
    """Configuration class to parse the config.yaml file contents."""

    """Configure AppConfig to point at the config.yaml file."""
    model_config = DriConfigConfigDict(
        config_folder=".",
        config_file_name="config.yaml",
    )

    parameter_a: str
    parameter_b: int
    parameter_c: float
    parameter_d: List[str]
    parameter_e: str = "default value"

app_config = AppConfig()

print(app_config.model_dump_json(indent=4))
"""
{
    "parameter_a": "some string",
    "parameter_b": 1,
    "parameter_c": 1.2,
    "parameter_d": [
        "I'm",
        "a",
        "list"
    ],
    "parameter_e": "default_value
}
"""

Now, if we add parameter_e to our config.yaml file:

# config.yaml

parameter_a: "some string"
parameter_b: 1
parameter_c: 1.2
parameter_d: ["I'm", "a", "list"]
parameter_e: "custom value"
We see how it prevails over the default value:
from typing import List

from driconfig import DriConfig, DriConfigConfigDict

class AppConfig(DriConfig):
    """Configuration class to parse the config.yaml file contents."""

    """Configure AppConfig to point at the config.yaml file."""
    model_config = DriConfigConfigDict(
        config_folder=".",
        config_file_name="config.yaml",
    )

    parameter_a: str
    parameter_b: int
    parameter_c: float
    parameter_d: List[str]
    parameter_e: str = "default value"

app_config = AppConfig()

print(app_config.model_dump_json(indent=4))
"""
{
    "parameter_a": "some string",
    "parameter_b": 1,
    "parameter_c": 1.2,
    "parameter_d": [
        "I'm",
        "a",
        "list"
    ],
    "parameter_e": "custom value"
}
"""