Skip to content

Lesson 7 — add configuration

What you will build

One setting, read from three places, to show which one wins. Nothing is hard-coded and nothing is global: configuration is read once, by the application, and reached through it.

What you need

The setup from lesson 1. This lesson uses nothing beyond the core package.

Where settings come from

Four sources, and the highest one in this list wins:

  1. explicit arguments passed in code,
  2. environment variables starting with XYBEROS_,
  3. the file xyberos.toml,
  4. defaults.

The code

examples/learn_fundamentals/fundamentals07_configuration.py
"""Lesson 7 — settings from a file, the environment, and arguments.

Run it with:

    python -m learn_fundamentals.fundamentals07_configuration
"""

from __future__ import annotations

import tempfile
from pathlib import Path

from xyberos import Settings

CONFIG_FILE = """\
[app]
name = "tutorial"
environment = "staging"
debug = true
"""


def main() -> None:
    with tempfile.TemporaryDirectory() as directory:
        (Path(directory) / "xyberos.toml").write_text(CONFIG_FILE, encoding="utf-8")

        settings = Settings.load(environ={}, cwd=Path(directory))
        print("from the file:", settings.app.name, settings.app.environment, settings.app.debug)

        settings = Settings.load(
            environ={"XYBEROS_APP__NAME": "from-the-environment"}, cwd=Path(directory)
        )
        print("the environment wins:", settings.app.name)

        settings = Settings.load(
            environ={"XYBEROS_APP__NAME": "from-the-environment"},
            overrides={"app": {"name": "from-the-arguments"}},
            cwd=Path(directory),
        )
        print("arguments win:", settings.app.name)


if __name__ == "__main__":
    main()

How to run it

$env:PYTHONPATH = "src;examples"
python -m learn_fundamentals.fundamentals07_configuration
PYTHONPATH=src:examples python -m learn_fundamentals.fundamentals07_configuration

What it prints

from the file: tutorial staging True
the environment wins: from-the-environment
arguments win: from-the-arguments

The same file is loaded three times with different inputs, which is why the module writes its xyberos.toml into a temporary directory instead of the working directory — a lesson that leaves a config file behind is a lesson that changes the next one's result.

The lines that matter

You write You get
[app] name = "tutorial" settings.app.name
XYBEROS_APP__NAME=tutorial the same setting, from the environment
XYBEROS_CONFIG=/etc/xyberos.toml a different file to read
Settings.load(overrides={"app": {"debug": True}}) an explicit argument, the highest precedence

The double underscore in XYBEROS_APP__NAME is not a typo: a single _ separates words inside a name, and __ walks into a section. So XYBEROS_APP__SHUTDOWN_TIMEOUT sets [app] shutdown_timeout.

Common mistakes

  • Looking for a global. There is no module-level settings object and no app.name shortcut. Read it through the application: app.settings.app.name.
  • Misspelling a key and expecting a default. An unknown key is an error, not silence. The message names the field and the source it came from, and never the value, because a value can be a secret.

  • Editing the file to change a deployment. That is what the precedence order is for: a deployment sets an environment variable and leaves everything under version control alone.

Exercise

Set the application name from the environment, without changing the file.

Show the answer === "PowerShell"
$env:XYBEROS_APP__NAME = "from-the-environment"
$env:PYTHONPATH = "src;examples"
python -m learn_fundamentals.fundamentals07_configuration
=== "POSIX"
XYBEROS_APP__NAME=from-the-environment PYTHONPATH=src:examples \
  python -m learn_fundamentals.fundamentals07_configuration
The file still says `tutorial`, and the first line of output still shows it — but the setting the lesson cares about now comes from the environment, which is how a deployment changes configuration without editing anything under version control.

Next

Lesson 8 is where the wiring becomes interesting: a service that needs another service, and what the runtime does when one is missing. Until it is published, the roadmap shows where each lesson stands.