Skip to content

Lesson 1 — what Xyberos is

What you will build

One application with one service in it. Small enough to read in a minute, and it already shows the three things everything later builds on: you compose an application, the runtime owns the lifetime of what you register, and code asks for what it needs instead of looking it up.

What you need

  • Python 3.10 or newer.
  • Xyberos, from PyPI. This lesson needs no extras.
python -m venv .venv
.venv\Scripts\python -m pip install xyberos

The code

examples/learn_fundamentals/fundamentals01_what_is_xyberos.py
"""Lesson 1 — what Xyberos is.

The smallest useful program: compose an application, register one service, run it for the duration
of a context manager, and ask for the service. Every name below is a documented import from the
stable surface, and nothing here needs an optional extra installed.
"""

from __future__ import annotations

import asyncio

import xyberos
from xyberos import App, Scope


class Greeter:
    """A service: an ordinary class that knows nothing about the runtime."""

    def __init__(self, name: str) -> None:
        self.name = name

    def greet(self) -> str:
        return f"hello from {self.name}"


async def main() -> None:
    """Print what the runtime is made of, using only the stable surface."""
    app = App()
    app.provide(Greeter, instance=Greeter("Xyberos"))

    print(f"xyberos {xyberos.__version__}")
    print(f"scopes: {', '.join(scope.value for scope in Scope)}")
    print(f"before start: {app.state.value}")

    async with app.lifespan():
        print(f"running: {app.state.value}")
        greeter = await app.resolve(Greeter)
        print(greeter.greet())
        print(f"providers: {len(app.diagnostics().providers)}")
        print(f"registered by: {app.diagnostics().providers[0].source}")

    print(f"after stop: {app.state.value}")


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

How to run it

From a clone of this repository, this file is the one the test suite runs:

python -m pip install -e .[http,database]
$env:PYTHONPATH = "src;examples"
python -m learn_fundamentals.fundamentals01_what_is_xyberos

In your own project, copy the file, then run it the same way — python -m pip install xyberos is the only thing it needs.

What it prints

xyberos 2.0.0
scopes: app, request, transient
before start: new
running: ready
hello from Xyberos
providers: 1
registered by: application
after stop: stopped

The first line is the version this page was verified against, and the test suite compares the whole block with the real output, so it cannot drift.

The lines that matter

Line What it does
app = App() creates an application: a composition root, not a container you look things up in
app.provide(Greeter, instance=Greeter("Xyberos")) registers a provider, keyed by its own class
async with app.lifespan(): starts the application, and stops it when the block ends
await app.resolve(Greeter) asks the application for the provider
app.state.value the lifecycle state: new, ready, and stopped here
app.diagnostics() where every provider came from, and in which order plugins started

Two of those are worth pausing on.

A provider is keyed by a type, not a name. Greeter is registered under Greeter, and nothing resolves a service by writing a string. That is why a typo in a contract is a failure at composition time rather than a surprise in production.

resolve is asynchronous. The await is not decoration: the runtime may have to construct the provider, and construction can be asynchronous.

Common mistakes

  • Forgetting await. app.resolve(Greeter) without await returns a coroutine, and the next line fails with 'coroutine' object has no attribute 'greet'.
  • Resolving outside the lifespan. app.resolve before async with app.lifespan(): raises LifecycleStateError, because the application has not started.
  • Reaching for a global. There is no module-level container. A service that needs another service declares it in its constructor as Annotated[Other, Inject()], which is lesson 8.

Exercise

Change the greeting so it names the person instead of the framework, and make the provider a class rather than a pre-built instance — app.provide(Greeter) with a name argument the runtime supplies is lesson 7's subject, so for now try Greeter("your name").

Next

Install Xyberos properly, with the extras each capability needs, in lesson 2 — install Xyberos. Until it is published, the roadmap shows where each lesson stands.