Lesson 11 — read xyberos check¶
What you will build¶
Nothing: this lesson reads the report for an application that already exists. xyberos check is the
same validation the application performs at startup — App.validate() — run without beginning to
start, which is what makes it usable in continuous integration.
What you need¶
Xyberos with the http extra, because the application being checked registers routes.
The application being checked¶
"""The application lesson 11 inspects, in the shape ``xyberos new`` writes.
A composition root, one service, and two routes — small enough to read, and enough for
``xyberos check`` to have something real to report: the provider graph, the provider sources, and
the handler signatures.
"""
from __future__ import annotations
from typing import Annotated
from xyberos import App, Inject
class Greeter:
"""A service with no HTTP types in it, so it can be tested on its own."""
def __init__(self, name: str) -> None:
self.name = name
def greet(self, who: str) -> str:
"""Return the greeting for ``who``."""
return f"{self.name} says hello, {who}"
def create_app() -> App:
"""Compose the application ``xyberos check`` is pointed at."""
app = App()
app.provide(Greeter, instance=Greeter("Xyberos"))
@app.get("/")
async def greet(greeter: Annotated[Greeter, Inject()]) -> dict[str, str]:
return {"greeting": greeter.greet("world")}
@app.get("/health")
async def health() -> dict[str, str]:
return {"status": "ok"}
return app
That is the shape xyberos new writes: a composition root, one service, and two routes. Nothing about
it is special, which is the point — check is useful on an ordinary application.
The command¶
What it prints¶
settings: xyberos (development), schema_version 1, debug False
application: fundamentals11_app:create_app
provider graph: valid
providers:
Greeter [app] from application
plugins:
(none)
routes: 2 (handler signatures valid)
Five answers, in the order a deployment wants them:
| Line | What it tells you |
|---|---|
settings: … |
which configuration was resolved, and from the discovered file — not the current directory |
application: … |
which target was called |
provider graph: valid |
App.validate() passed, so nothing is missing and no scope rule is broken |
providers: … |
the same report lesson 10 printed from code, including the sources |
routes: 2 (handler signatures valid) |
every handler was compiled, so each parameter is injectable |
The last line is the one you cannot get from validate() alone: route signatures are checked by the
HTTP adapter, and a handler that declares something the adapter cannot supply is a startup failure.
check reports it without binding a port.
The flags worth knowing¶
| Option | Why it exists |
|---|---|
--app module:attribute |
a factory that returns an App; it is called, and nothing is started |
--app-dir DIR |
makes a src layout importable without installing it |
--config PATH |
checks a specific settings file instead of the discovered one |
Common mistakes¶
- Pointing
--appat the ASGI application.checksays so explicitly rather than guessing: an ASGI application is built from anAppwithHttpAdapter(app).asgi_app, so point it at theAppor at the factory that builds one. - Running it without the extra the application needs. A missing extra reports the extra to install;
checknever silently skips a route it cannot compile. - Expecting it to catch runtime failures. A valid graph is not a working application: a provider whose factory raises on construction is only discovered when something is actually built.
Exercise¶
Run check with no --app at all, and read what it does not claim.
Show the answer
The report stops after the settings line and says `application: not inspected (pass --app module:attribute)`. It does not pretend to have validated an application it never looked at — which is the habit worth taking from this lesson: a report should say what it checked, and what it did not.Next¶
Lesson 12 is about where code goes: the one convention that decides which module may import the framework. Until it is published, the roadmap shows where each lesson stands.