Custom extensions¶
Extensions are services that plug into the application: they can register controllers, middleware, exception handlers, CLI commands, template folders and lifecycle hooks. The official extension packages are built this way.
from pydantic import BaseModel
from patera import BaseExtension, Patera
class AuditConfig(BaseModel):
ENABLED: bool = True
class AuditService(BaseExtension[Patera, AuditConfig]):
def init(self) -> None:
if self.configs.ENABLED:
self.app.add_on_startup_method(self.start)
self.app.add_on_shutdown_method(self.stop)
async def start(self) -> None:
self.app.logger.info("Audit started")
async def stop(self) -> None:
self.app.logger.info("Audit stopped")
Declare AUDIT_SERVICE: AuditConfig = AuditConfig() on the application's config for nested
environment settings, and register the class in app_extensions or inject it. The two generic
parameters are the application type, then the configuration type.
Construction and init()¶
Construction resolves injections, registers the instance, then calls the synchronous init().
Prefer that hook over overriding __init__. In init(), use the application's public methods:
| Register | Method |
|---|---|
| controller classes | app.register_controller(...) |
| middleware classes (before the chain is built) | app.register_middleware(cls, order) |
| exception handler classes | app.register_exception_handler(...) |
| CLI controller instances | app.register_cli_controller(instance) |
| template folders | app.add_template_path(path), app.add_template_prefix(prefix, path) |
| lifecycle callbacks | app.add_on_startup_method(fn), app.add_on_shutdown_method(fn) |
| request observers | app.add_request_observer(fn) |
Registration is provisional until init() succeeds. A failed construction rolls back framework
registries, routes, template paths and lifecycle hooks, including nested dependency
registrations, and injection rejects instances still initializing. This cannot undo arbitrary
external side effects: acquire resources in startup, or clean them up yourself if initialization
fails.
Guidelines¶
- Acquire resources in startup and release them in shutdown. CLI users must manage resources explicitly, because commands do not run the lifespan.
- Keep request state in the request context, never on the instance.
- Keep instances independent, so several test applications can coexist.
- Validate settings rather than silently ignoring invalid values.
- Extension class names must not collide within one application.
- Do not import the concrete application into a reusable extension; type against
Patera.
Publishing an extension¶
Publish reusable extensions as separate distributions. Official packages install under the
patera namespace, whose path is extended by the core; a third-party package can use its own
import namespace. Include templates and static resources in the distribution and test an
installed artifact, not only editable sources. Follow the naming conventions in the
public API reference.