Skip to content

Lesson 5 — run your application

What you will build

The same application as lesson 4, run the way a deployment runs it: configuration read from a file, startup and shutdown controlled explicitly rather than by a context manager, and the lifecycle state visible at each step.

What you need

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

The code

examples/learn_fundamentals/fundamentals05_run.py
"""Lesson 5 — running an application the way a deployment does.

Configuration is read from a file, the application is started and stopped explicitly rather than
through a context manager, and the lifecycle state is visible at each step. The file is written into
a temporary directory: a lesson that leaves a config file behind changes the next lesson's result.
"""

from __future__ import annotations

import asyncio
import tempfile
from pathlib import Path

from xyberos import App, Settings

CONFIG = """\
[app]
name = "lesson"
environment = "production"
debug = false
"""


async def main() -> None:
    """Load settings from a file, then start and stop the application by hand."""
    with tempfile.TemporaryDirectory() as directory:
        location = Path(directory) / "xyberos.toml"
        location.write_text(CONFIG, encoding="utf-8")

        settings = Settings.load(environ={}, cwd=Path(directory))
        print("configuration read from:", location.name)
        print("environment:", settings.app.environment)

        app = App(settings=settings)
        print("before start:", app.state.value)
        await app.start()
        print("after start:", app.state.value)
        await app.stop()
        print("after stop:", app.state.value)


if __name__ == "__main__":
    asyncio.run(main())

How to run it

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

The module writes its xyberos.toml into a temporary directory: a lesson that leaves a settings file behind changes the next lesson's result, and a configuration lesson in particular must not depend on what a previous run wrote.

What it prints

configuration read from: xyberos.toml
environment: production
before start: new
after start: ready
after stop: stopped

The lines that matter

Line What it does
Settings.load(environ={}, cwd=Path(directory)) reads xyberos.toml from that directory, with no environment overrides
App(settings=settings) composes the application around the settings you resolved
await app.start() validates, freezes the graph, and builds every eager provider
await app.stop() stops accepting work, then releases what the application owns
app.state.value new, ready, and stopped — the three states this run passes through

async with app.lifespan() is start() and stop(). Lesson 4 used the context manager, which is the form to prefer; this lesson uses the two calls so you can see the states between them. The context manager also stops the application when the block raises, which is the reason to prefer it.

Configuration is read once, by the application. app.settings is the only place to reach it, and nothing in a service reads the environment for itself.

Common mistakes

  • Starting an application twice. The second start() returns immediately when the state is already ready — it is not an error, and it is not a second startup either.
  • Forgetting to stop. A stopped process releases the operating system's resources anyway, but an application that is stopped releases its own: connection pools, file handles, and background tasks. In a test, that is the difference between a suite that passes twice and one that passes once.
  • Reading the environment in a service. That is what lesson 7 is for, and doing it in a service is how configuration becomes untestable.

Exercise

Change the settings file so the environment is staging, and predict which lines of output change.

Show the answer Only the second line changes: `environment: staging`. The states are the same, because the lifecycle does not depend on configuration — and the first line does not change either, because the file is still found by its name in the directory `cwd` points at. If you instead set `XYBEROS_APP__ENVIRONMENT=staging` in the environment, nothing changes here: the module passes `environ={}`, deliberately, so that its output does not depend on the operator's shell. Removing that empty mapping is what lesson 7 changes. Then the environment wins.

Next

Lesson 6 takes the same start-and-stop sequence and asks what happens when startup fails halfway. Until it is published, the roadmap shows where each lesson stands.