Skip to content

Lesson 10 — understand the provider registry

What you will build

Nothing new — the lesson is about reading what you have already built. app.diagnostics() reports every registered provider, its scope, whether it is eager, and where it came from, which is the first thing to look at when a wiring question has no obvious answer.

What you need

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

Three verbs, and no fourth

Verb Call What it means
Register app.provide(Contract, ...) "this is what answers Contract"
Resolve Annotated[Contract, Inject()] or app.resolve(Contract) "give me what the application registered"
Inspect app.diagnostics() "show me what is registered, and who registered it"

There is no fourth verb, and that is deliberate: nothing looks a provider up by name, so a typo is a type error rather than a runtime surprise.

The code

examples/learn_fundamentals/fundamentals10_registry.py
"""Lesson 10 — reading the provider registry.

`app.provide` is the registration, `Inject()` is the resolution, and `app.diagnostics()` is the view
you debug with: every provider, its scope, whether it is eager, and where it came from. Xyberos
reports the source of a provider because "which plugin registered this?" is the first question a
wiring problem asks.
"""

from __future__ import annotations

import asyncio

from xyberos import App, Scope


class Greeter:
    """An ordinary service, registered as a pre-built instance."""

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

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


class RequestState:
    """A service whose lifetime is one request."""


async def main() -> None:
    """Register two providers and read the registry's own report."""
    app = App()
    app.provide(Greeter, instance=Greeter("Xyberos"))
    app.provide(RequestState, scope=Scope.REQUEST)
    print(app.diagnostics().render())


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

How to run it

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

What it prints

providers:
  Greeter [app] from application
  RequestState [request] from application
plugins:
  (none)

Read one line at a time and it is a table with four columns: the contract, its scope, whether it is eager, and its source.

Field Why it is reported
Greeter the contract the provider answers, which is the key everything else resolves against
[app] the scope: app, request, or transient — see lesson 9
from application the source: application, or the plugin that registered it
(none) there are no plugins, and the report says so rather than omitting the section

The source column is the one that earns its keep in a real application: "which plugin registered this provider?" is the first question a wiring problem asks, and the answer is not deducible once several plugins are loaded.

Common mistakes

  • Reading the report before the graph is validated. It shows what is registered, not what is resolvable: app.validate() is the check, and the report is the map.
  • Expecting a plugin's providers to be anonymous. They are reported with the plugin as their source, which is why xyberos check prints the same report for a real application.
  • Treating the report as an API. It is a human-readable summary for troubleshooting. Code that needs the facts should read app.diagnostics().providers, which is a tuple of typed records.

Exercise

Register a third provider with eager=True, then predict its line before running the module.

Show the answer The new line gains `eager` inside the brackets — `[app, eager]` where the other two say `[app]` — and it appears last, because the report follows registration order rather than sorting. Nothing about the other lines changes, which is worth noticing: the report describes the graph you registered, and adding a provider does not reorder or annotate the ones beside it.

Next

Lesson 11 takes the same report out of your code and into the command line, where it belongs in a deployment. Until it is published, the roadmap shows where each lesson stands.