Plugins
The plugin mixin and the OpenAPI plugin (router integration and translator).
Plugin capability: router plugins as a mixin over the base server (D16).
The base server knows nothing about router plugins. This mixin adds them as a
capability, composed BEFORE the server class (class MyServer(PluginMixin,
BaseServer)), mirroring MiddlewareMixin: its cooperative __init__
peels plugins= (the {name: bool | dict} switches — a dict value enables
the plugin and becomes its plug options) and plugin_registry= (extra
{name: class} entries merged over default_plugin_registry()), and
exposes arm_router(router).
Unlike the middleware chain — assembled once around the base dispatch —
plugins are armed onto the ROUTER of each routed application: arm_router
is called by RoutedApplication on first route access. Per enabled
plugin it ensures the plugin class is registered with genro-routes (idempotent),
then batch-plugs the enabled set with a single router.plug([...]) call
(genro-routes 0.28.0 accepts a list of plugin dicts); bundled genro-routes
plugins (auth/channel/env/logging/pydantic) carry no class in the registry and
are plugged by name alone. A composition WITHOUT this mixin exposes no
arm_router and arms nothing — RoutedApplication degrades silently.
default_plugin_registry() returns a FRESH dict per call ({"openapi":
OpenAPIPlugin} as of Phase 5) — a function, so no module-level mutable
registry exists; and importing this module never registers a plugin against
genro-routes (no import side effect).
- class genro_asgi.plugin_mixin.PluginMixin(**kwargs)[source]
Bases:
objectRouter-plugin capability mixin, composed BEFORE a server class.
Every server arms the
FIXED_PLUGINS(pydantic/openapi) unconditionally — the fixed structure of a routed core. Constructor kwargs peeled here:plugins— the{name: bool | dict}switches tuning the base and adding extras (a dict value becomes the plug options;Falsedrops an EXTRA but is a config error on a fixed plugin);plugin_registry— extra{name: class}entries merged overdefault_plugin_registry().- Parameters:
kwargs (Any)
- property plugins: dict[str, dict[str, Any]]
The materialized
{name: options}config of the enabled plugins.
- property plugin_registry: dict[str, type[BasePlugin]]
The effective
{name: class}registry (defaults + extras).
- arm_router(router)[source]
Register and plug every enabled plugin onto
router(idempotent).A plugin carrying a class in the registry is registered with genro-routes first (guarded — never re-registered); the not-yet-attached plugins are then armed with a single batch
router.plug([...])call, so arming twice is safe (already-attached plugins are skipped). Unknown plugin names surface asrouter.plugerrors, not silent no-ops.- Return type:
- Parameters:
router (Router)
- genro_asgi.plugin_mixin.default_plugin_registry()[source]
A fresh
{name: class}mapping of the plugins this core arms itself.Only the transport-dialect plugins that live in this package need a class here (so
arm_routercan register them). genro-routes’ own bundled plugins are plugged by name without a class.
OpenAPIPlugin — per-handler OpenAPI configuration.
Provides explicit control over OpenAPI schema generation for handlers. Use this plugin to override automatically guessed HTTP methods or add OpenAPI-specific metadata like tags, summary, description, and security.
- Accepted config keys (router-level or per-handler):
enabled: gate the plugin entirely (default True)method: HTTP method override (e.g. “get”, “post”, “delete”)tags: OpenAPI tags (string or list of strings)summary: summary override for the operationdescription: description override for the operationdeprecated: mark the operation as deprecated (default False)security_scheme: security scheme name (default “BearerAuth”)security: explicit security override (list, or [] for public)
- Cross-plugin integration (read by
OpenAPITranslator): When the auth plugin is active,
securityis auto-derived fromauth_rule.When the env plugin is active,
x-requiresis auto-derived fromenv_requires.
Unlike the old genro-asgi module, this file does NOT register itself against
genro-routes at import time (the no-global-state / no-import-side-effect rule):
registration is an explicit arming act performed by PluginMixin.arm_router.
- class genro_asgi.plugins.openapi.plugin.OpenAPIPlugin(router, **config)[source]
Bases:
BasePluginOpenAPI plugin for explicit schema control.
By default HTTP methods are guessed from the signature (GET for scalar params, POST for complex types). Use this plugin to override the guessed method or add OpenAPI-specific metadata.
- Parameters:
router (Any)
config (Any)
OpenAPITranslator — turn genro-routes nodes() output into OpenAPI.
Reads the dialect-neutral description produced by router.nodes() and NEVER
re-derives anything from the callables. genro-routes’ pydantic plugin computes
and caches, once at decoration time, both the input and the output description
of every handler; this translator only reads those neutral blocks:
input —
entry_info["params"]:schemais the aggregate request JSON schema (request_schema) andfieldsis the per-parameter list ({name, schema, required, default, kind}). The HTTP method is guessed from the per-parameter schemas (all scalar → GET, else POST), and the query parameters / request body are built from the cachedschema— no pydantic model is ever built here;output —
entry_info["result"]:{schema, media_type}(the return-type schema the pydantic plugin produced viaTypeAdapter);security/x-requirescome from the per-entryauth/envplugin config.
Consequently this module imports NO pydantic (and does not inspect callables):
pydantic is a concern of genro-routes’ pydantic plugin, which owns the schema
derivation. The cached schema blocks keep $defs inline (self-contained
per entry); this translator lifts them into a document-level pool, as OpenAPI
expects — copying each block before popping $defs so the plugin’s cache is
never mutated.
The translator methods are staticmethods by design (the granted builder-style exception, coding rule 4): pure data-logic functions over the neutral node description, holding no instance state.
- class genro_asgi.plugins.openapi.translator.OpenAPITranslator[source]
Bases:
objectTranslate router
nodes()output to OpenAPI format.- Modes:
openapi: flat format, all paths merged into a single paths dict.h_openapi: hierarchical format preserving the router tree.
- static translate_openapi(nodes_data, lazy=False, path_prefix='')[source]
Translate
nodes()output to flat OpenAPI format.
- static translate_h_openapi(nodes_data, lazy=False, path_prefix='')[source]
Translate
nodes()output to hierarchical OpenAPI format.
- static entry_info_to_openapi(name, entry_info)[source]
Convert an entry info dict to an OpenAPI path item.
HTTP method priority: explicit openapi plugin config, else guessed from the per-parameter schemas. Input (query params / request body) is built from the cached
paramsblock; the response schema is read from the neutralresultblock. Nothing is derived from the callable.
- static guess_http_method(fields)[source]
Guess the HTTP method from the neutral parameter fields.
GET when every typed parameter carries a scalar JSON schema, else POST. Reads only the cached
fields(never the callable); an untyped parameter —schemaisNone(unannotated, or*args/**kwargs) — does not force POST, matching “no typed params → GET”.