Core¶
The application bootstrap, DI container, config, routing, service providers, the application-level event dispatcher, view rendering, and the framework's exception/validation primitives.
application ¶
The Zeython application: an ASGI app assembled from service providers.
Application ¶
The central object: owns the container, config, router, and providers.
Application is itself a valid ASGI callable, so uvicorn main:app
works directly once you construct one and register at least a router.
Source code in src/zeython/application.py
container ¶
A minimal, type-hint-driven dependency injection container.
Zeython's :class:Container resolves dependencies by inspecting constructor
and function type annotations, in the spirit of Laravel's service container.
Bindings can be a plain instance, a factory callable, or a class to
autowire directly.
BindingResolutionError ¶
Bases: Exception
Raised when the container cannot resolve a requested binding.
Container ¶
A small service container supporting binding, singletons, and autowiring.
Source code in src/zeython/container.py
bind ¶
Register a binding. If factory is omitted, abstract must be a concrete class.
Source code in src/zeython/container.py
singleton ¶
instance ¶
make ¶
Resolve abstract to a concrete instance, autowiring its dependencies.
Source code in src/zeython/container.py
call ¶
Call fn, resolving any missing arguments from the container.
Source code in src/zeython/container.py
config ¶
Environment-driven configuration for Zeython applications.
Config ¶
Layered configuration backed by the environment and .env files.
Values resolve in this order (highest priority first): real process
environment variables, then the loaded .env file, then explicit
defaults passed to :meth:get.
Source code in src/zeython/config.py
get ¶
Dot-path lookup, e.g. config.get("database.url") reads DATABASE_URL.
providers ¶
Service providers: the seam where cross-cutting concerns hook into boot.
ServiceProvider ¶
Base class for registering and booting application services.
register() runs for every provider before any provider's boot()
runs, so bindings you depend on in boot() are guaranteed to exist
regardless of registration order.
Source code in src/zeython/providers.py
register ¶
DatabaseServiceProvider ¶
Bases: ServiceProvider
Wires up the async :class:~zeython.db.Database and its request-scoped session.
DATABASE_POOL_SIZE/DATABASE_MAX_OVERFLOW are passed straight
through to SQLAlchemy's connection pool when set -- unset by default,
so nothing changes for an in-memory SQLite URL (:memory:), whose
default pool doesn't accept them at all. See
docs/database.md#connection-pooling.
DATABASE_READ_URL, if set, binds a read replica --
database.read_replica() opens a session against it instead of the
primary. See docs/database.md#read-replicas.
Source code in src/zeython/providers.py
RouteServiceProvider ¶
Bases: ServiceProvider
Imports route modules for their side effect of registering routes on the app.
Source code in src/zeython/providers.py
ViewServiceProvider ¶
Bases: ServiceProvider
Binds a :class:~zeython.views.Views instance for server-rendered HTML.
Looks for templates in resources/views under the app's base path by
default; override with VIEWS_PATH in .env or the views.path
config key.
Source code in src/zeython/providers.py
CorsServiceProvider ¶
Bases: ServiceProvider
Opt-in CORS support, configured via .env.
CORS_ORIGINS— comma-separated list of allowed origins (default: none)CORS_ALLOW_CREDENTIALS— defaultfalseCORS_ALLOW_METHODS— comma-separated, default*CORS_ALLOW_HEADERS— comma-separated, default*
Source code in src/zeython/providers.py
events ¶
Application-level events: decoupled listeners reacting to a domain event
(OrderPlaced, UserRegistered, ...) without the code that raises it
needing to know who's listening.
Deliberately separate from :class:zeython.db.Observer -- an Observer
reacts to one model's own lifecycle (creating, updated, ...); an
event here can be anything your application defines, dispatched from
anywhere (a controller, a job, a scheduled task), with any number of
independent listeners reacting to it without editing the code that
dispatches it. A common pattern is dispatching an event from a model
hook (created()) once the write itself is the model's own concern but
what happens next (send a receipt, notify a webhook, update a search
index) isn't.
EventDispatcher ¶
Maps an event type to the listeners registered for it.
Source code in src/zeython/events.py
listen ¶
Register listener to run whenever an instance of event_type is dispatched.
on ¶
Decorator form of :meth:listen::
@dispatcher.on(OrderPlaced) async def send_receipt(event: OrderPlaced) -> None: ...
Source code in src/zeython/events.py
listeners_for ¶
The listeners currently registered for event_type, in registration order.
dispatch
async
¶
Call every listener registered for type(event), in registration order.
A listener's own exception is logged and reported (see
:mod:zeython.error_monitoring), not raised -- one broken listener
(a bad webhook call, a typo in an audit-log write) shouldn't stop
the others from running, the same way a failed Slack notification
shouldn't also silently swallow the receipt email.
Source code in src/zeython/events.py
EventServiceProvider ¶
Bases: ServiceProvider
Binds an :class:EventDispatcher into the container.
Register your own listeners by subclassing and overriding boot()
(calling super().boot() first, so the dispatcher exists) --
registration happens once, at startup, the same way route modules and
other providers wire themselves up::
class AppEventServiceProvider(EventServiceProvider):
def boot(self) -> None:
super().boot()
dispatcher = self.container.make(EventDispatcher)
dispatcher.listen(OrderPlaced, send_receipt_email)
dispatcher.listen(OrderPlaced, notify_fulfillment_webhook)
Source code in src/zeython/providers.py
emit
async
¶
Dispatch event to every listener registered for its type.
Uses whichever :class:EventDispatcher is bound in the container (see
:class:EventServiceProvider). Outside of a request -- a job, a
scheduled task, a model hook -- dispatch directly against a resolved
dispatcher instead::
await app.container.make(EventDispatcher).dispatch(event)
Source code in src/zeython/events.py
routing ¶
Ergonomic routing built on top of Starlette's proven route matching.
Controller ¶
Marker base class for class-based controllers used with :meth:Router.resource.
Router ¶
Collects routes and exposes Laravel/FastAPI-style decorator sugar.
A Router compiles down to a plain list of Starlette BaseRoute
objects, so nesting via :meth:include is just a Mount and gets the
same battle-tested path matching as everything else built on Starlette.
Source code in src/zeython/routing.py
websocket ¶
Register a WebSocket handler: async def handler(websocket: WebSocket) -> None.
See :mod:zeython.websockets and docs/websockets.md.
Source code in src/zeython/routing.py
include ¶
Mount another router's routes under an optional additional prefix.
mount ¶
Mount an arbitrary ASGI app (e.g. starlette.staticfiles.StaticFiles) at a path prefix.
resource ¶
resource(
path: str,
controller_cls: type[Controller],
*,
only: Iterable[str] | None = None,
) -> None
Register RESTful CRUD routes bound to a controller's methods.
Maps: index->GET path, store->POST path, show->GET path/{id}, update->PUT/PATCH path/{id}, destroy->DELETE path/{id}.
Source code in src/zeython/routing.py
views ¶
Server-rendered HTML views, by convention read from resources/views/.
Views ¶
Thin wrapper around Starlette's Jinja2 integration.
Bound into the container by :class:~zeython.providers.ViewServiceProvider
under the resources/views directory by default. Use the module-level
:func:render helper from inside a controller/handler for Flask-style
ergonomics.
Source code in src/zeython/views.py
render ¶
render(
request: Request,
name: str,
context: dict[str, Any] | None = None,
*,
status_code: int = 200,
) -> HTMLResponse
Render name using the application's registered :class:Views instance.
Usage inside a controller::
from zeython.views import render
async def show(self, request):
return render(request, "posts/show.html", {"post": post})
Source code in src/zeython/views.py
exceptions ¶
HTTP-aware exception hierarchy with a default JSON error handler.
HTTPException ¶
Bases: Exception
Base class for exceptions that should be rendered as HTTP responses.
Source code in src/zeython/exceptions.py
validation ¶
Declarative validation rules for :class:zeython.db.Model fields.
Rule ¶
validate ¶
Run declarative rules against a plain dict -- the same rule sets you'd
write for :attr:zeython.db.Model.__rules__, applied to a request
payload, query params, or any other dict that isn't (or isn't yet) a
model instance. Does not raise; raise ValidationException(errors)
yourself if that's what you want when errors is non-empty.
Model.validate() is this function applied to a model instance's own
field values -- kept in sync with it deliberately, so a rule set means
the same thing whether it's checked against a model or a plain dict.