Skip to content

Introduction#

dature offers two ways to load configuration: function mode and decorator mode.

common_app.yaml
host: localhost
port: 8080
debug: false
"""Load from YAML file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SHARED_DIR = Path(__file__).parents[1] / "shared"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.Yaml12Source(file=SHARED_DIR / "common_app.yaml"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
common_app.yaml
host: localhost
port: 8080
debug: false
"""Decorator mode — auto-load config from a YAML file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SHARED_DIR = Path(__file__).parents[1] / "shared"


@dature.load(dature.Yaml12Source(file=SHARED_DIR / "common_app.yaml"))
@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = Config()

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False

Explicit arguments to __init__ take priority over loaded values:

config = Config(port=9090)  # host from source, port overridden

All Formats#

Use the specific Source subclass for your format. Here's the same config loaded from every supported format:

common_app.yaml
host: localhost
port: 8080
debug: false
"""Load from YAML file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SHARED_DIR = Path(__file__).parents[1] / "shared"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.Yaml12Source(file=SHARED_DIR / "common_app.yaml"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
intro_app.json
{
  "host": "localhost",
  "port": 8080,
  "debug": false
}
"""Load from JSON file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.JsonSource(file=SOURCES_DIR / "intro_app.json"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
intro_app.json5
{
  // JSON5 supports comments
  host: "localhost",
  port: 8080,
  debug: false,
}
"""Load from JSON5 file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.Json5Source(file=SOURCES_DIR / "intro_app.json5"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
intro_app.toml
host = "localhost"
port = 8080
debug = false
"""Load from TOML file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.Toml11Source(file=SOURCES_DIR / "intro_app.toml"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
intro_app.ini
[app]
host = localhost
port = 8080
debug = false
"""Load from INI file with section prefix."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.IniSource(file=SOURCES_DIR / "intro_app.ini", prefix="app"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
intro_app.env
HOST=localhost
PORT=8080
DEBUG=false
"""Load from ENV file."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.EnvFileSource(file=SOURCES_DIR / "intro_app.env"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False
app_docker_secrets/
├── host    → "localhost"
├── port    → "8080"
└── debug   → "false"
"""Load from Docker secrets directory."""

from dataclasses import dataclass
from pathlib import Path

import dature

SOURCES_DIR = Path(__file__).parent / "sources"


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


config = dature.load(
    dature.DockerSecretsSource(dir_=SOURCES_DIR / "intro_app_docker_secrets"),
    schema=Config,
)

assert config.host == "localhost"
assert config.port == 8080
assert config.debug is False

See the full list of Source classes and their extra dependencies on the main page.

Source Reference#

@dataclass(kw_only=True, repr=False)
class Source(abc.ABC):
    prefix: "DotSeparatedPath | None" = None
    name_style: "NameStyle | None" = None
    field_mapping: "FieldMapping | None" = None
    root_validators: "Iterable[RootPredicate] | None" = None
    validators: "FieldValidators | None" = None
    expand_env_vars: "ExpandEnvVarsMode | None" = None
    skip_if_broken: bool | None = None
    skip_field_if_invalid: "bool | tuple[FieldPath, ...] | None" = None
    type_loaders: "TypeLoaderMap | None" = None
    tag: str | None = None
    when: "Mapping[str, str | tuple[str, ...]] | None" = None

    format_name: ClassVar[str]
    location_label: ClassVar[str]
    config_group: ClassVar[str | None] = None

    retorts: "dict[tuple[type, frozenset[tuple[type, Any]]], Retort]" = field(
        default_factory=dict,
        init=False,
        repr=False,
    )
Parameter Description
prefix Filter ENV keys ("APP_") or extract nested object ("app.database")
name_style Naming convention mapping. See Naming
field_mapping Explicit field renaming with F objects. See Naming
root_validators Post-load validation of the entire object. See Validation
validators Per-field validators in metadata. See Validation
expand_env_vars ENV variable expansion mode. See Advanced — ENV Expansion
skip_if_broken Skip this source if it fails to load. See Advanced — Skipping Broken Sources
skip_field_if_invalid Skip invalid fields from this source. See Advanced — Skipping Invalid Fields
type_loaders Custom type converters for this source. See Custom Types & Loaders

FileSource subclasses (JsonSource, Yaml*Source, Toml*Source, IniSource, Json5Source) also have:

Parameter Description
file Path to config file (str, Path) or file-like object (BytesIO, StringIO). None → empty path

FlatKeySource subclasses (EnvSource, EnvFileSource, DockerSecretsSource) also have:

Parameter Description
nested_sep Delimiter for flat→nested conversion. Default: "__"
nested_resolve_strategy Priority when both JSON and flat keys exist for a nested field: "flat" (default) or "json". See Nested Resolve
nested_resolve Per-field strategy overrides using F objects. Takes priority over nested_resolve_strategy. See Nested Resolve

File-Like Objects#

file accepts file-like objects (StringIO, BytesIO, and any TextIOBase/BufferedIOBase/RawIOBase subclass):

"""Load from file-like objects (BytesIO, StringIO)."""

from dataclasses import dataclass
from io import BytesIO, StringIO

import dature


@dataclass
class Config:
    host: str
    port: int
    debug: bool = False


# From StringIO
text_stream = StringIO('{"host": "localhost", "port": 8080, "debug": true}')
config = dature.load(dature.JsonSource(file=text_stream), schema=Config)

assert config.host == "localhost"
assert config.port == 8080

# From BytesIO
binary_stream = BytesIO(b'{"host": "0.0.0.0", "port": 3000}')
config = dature.load(dature.JsonSource(file=binary_stream), schema=Config)

assert config.host == "0.0.0.0"
assert config.port == 3000

Note

EnvSource and DockerSecretsSource do not support file-like objects — they read from environment variables and directories respectively.

Type Coercion#

String values from ENV and file formats are automatically converted. See Supported Types for the full list and Custom Types & Loaders for custom type parsers and format loaders. Error output with source location is covered in Validation — Error Format.