tigrbl 0.3.16.dev2__tar.gz → 0.3.16.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/PKG-INFO +1 -1
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/pyproject.toml +1 -1
- tigrbl-0.3.16.dev4/tigrbl/api/_router.py +490 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/transport.py +1 -17
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/jsonrpc/dispatcher.py +32 -0
- tigrbl-0.3.16.dev2/tigrbl/api/_router.py +0 -273
- tigrbl-0.3.16.dev2/tigrbl/app/_routing_runtime.py +0 -24
- tigrbl-0.3.16.dev2/tigrbl/core/router_runtime.py +0 -164
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/LICENSE +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/_api.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/_route.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/_routing.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/api_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/resolve.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/api/tigrbl_api.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/_app.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/_model_registry.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/app_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/app/tigrbl_app.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/api/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/api/common.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/api/include.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/api/resource_proxy.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/api/rpc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/columns.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/builder.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/ctx.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/identifiers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/namespaces.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/handlers/steps.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/hooks.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/model.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/model_helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/model_registry.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/attach.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/collection.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/common.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/fastapi.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/io.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/io_headers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/member.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/router.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rest/routing.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/rpc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/schemas/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/schemas/builder.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/schemas/defaults.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/bindings/schemas/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/_column.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/column_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/field_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/core.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/jsonhints.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/planning.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/infer/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/io_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/column/storage_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/config/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/config/constants.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/config/defaults.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/config/resolver.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/bulk.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/db.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/enum.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/filters.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/model.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/helpers/normalize.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/ops.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/crud/params.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/core/resolver.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/ddl/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/deps/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/deps/jinja.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/deps/pydantic.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/deps/sqlalchemy.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/docs/verbosity.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/_engine.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/bind.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/builders.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/capabilities.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/docs/PLUGINS.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/engine_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/plugins.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/registry.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/resolver.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/engine/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/headers/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/headers/_header.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/_hook.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/exceptions.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/hook_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/hook/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/base.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/compose.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/cors.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/middleware.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/middlewares/spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/_op.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/canonical.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/model_registry.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/resolver.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/op/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/_RowBound.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/bootstrappable.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/bound.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/edges.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/fields.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/hierarchy.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/key_digest.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/lifecycle.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/locks.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/markers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/operations.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/ownable.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/principals.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/tenant_bound.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/upsertable.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/mixins/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/_base.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/audit.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/client.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/group.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/org.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/rbac.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/status.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/tenant.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/orm/tables/user.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/requests/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/requests/_request.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/requests/adapters.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/_response.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/_transport.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/bind.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/resolver.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/responses/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/rest/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/emit/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/emit/paired_post.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/emit/paired_pre.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/emit/readtime_alias.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/out/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/out/masking.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/refresh/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/refresh/demand.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/resolve/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/resolve/assemble.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/resolve/paired_gen.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/headers_from_payload.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/negotiate.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/negotiation.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/render.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/renderer.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/template.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/response/templates.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/schema/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/schema/collect_in.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/schema/collect_out.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/storage/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/storage/to_stored.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/wire/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/wire/build_in.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/wire/build_out.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/wire/dump.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/atoms/wire/validate_in.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/context.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/events.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/executor/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/executor/guards.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/executor/helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/executor/invoke.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/executor/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/kernel.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/labels.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/opview.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/ordering.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/status/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/status/converters.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/status/exceptions.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/status/mappings.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/status/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/system.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/runtime/trace.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/_schema.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/build_schema.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/cache.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/compat.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/extras.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/list_params.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/builder/strip_parent_fields.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/get_schema.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/schema_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/types.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/schema/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/dependencies.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/_base.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/api_key.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/http_bearer.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/mutual_tls.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/oauth2.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/security/schemes/openid_connect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/README.md +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/abc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/base.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/default.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/session/spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/specs.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/compat.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/healthz.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/hookz.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/kernelz.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/methodz.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/router.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/diagnostics/utils.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/lens.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openapi/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openapi/helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openapi/metadata.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openapi/mount.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openapi/schema.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/openrpc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/docs/swagger.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/favicon/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/favicon/assets/favicon.svg +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/system/uvicorn.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/_base.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/_table.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/mro_collect.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/shortcuts.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/table/table_spec.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/background.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/httpx.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/jsonrpc/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/jsonrpc/helpers.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/jsonrpc/models.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/jsonrpc/openrpc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/rest/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/rest/aggregator.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/transport/rest/decorators.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/__init__.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/allow_anon_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/authn_abc.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/nested_path_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/op.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/op_config_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/op_verb_alias_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/request_extras_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/response_extras_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/table_config_provider.py +0 -0
- {tigrbl-0.3.16.dev2 → tigrbl-0.3.16.dev4}/tigrbl/types/uuid.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrbl
|
|
3
|
-
Version: 0.3.16.
|
|
3
|
+
Version: 0.3.16.dev4
|
|
4
4
|
Summary: A modern pure ASGI/WSGI Python framework for building schema-first REST and JSON-RPC APIs with SQLAlchemy models, typed validation, lifecycle hooks, and engine extension support.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tigrbl"
|
|
3
|
-
version = "0.3.16.
|
|
3
|
+
version = "0.3.16.dev4"
|
|
4
4
|
description = "A modern pure ASGI/WSGI Python framework for building schema-first REST and JSON-RPC APIs with SQLAlchemy models, typed validation, lifecycle hooks, and engine extension support."
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""Router primitives backing ``tigrbl.api.Api`` and ``tigrbl.app.App``.
|
|
2
|
+
|
|
3
|
+
This compatibility router surface is slated for deprecation in favor of the
|
|
4
|
+
higher-level ``Api``/``App`` interfaces.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import inspect
|
|
10
|
+
from contextlib import asynccontextmanager
|
|
11
|
+
from typing import Any, Callable
|
|
12
|
+
|
|
13
|
+
from tigrbl.api._routing import (
|
|
14
|
+
add_api_route,
|
|
15
|
+
include_router,
|
|
16
|
+
merge_tags,
|
|
17
|
+
normalize_prefix,
|
|
18
|
+
route,
|
|
19
|
+
)
|
|
20
|
+
from tigrbl.api.resolve import (
|
|
21
|
+
invoke_dependency as _invoke_dependency_impl,
|
|
22
|
+
resolve_handler_kwargs as _resolve_handler_kwargs_impl,
|
|
23
|
+
resolve_route_dependencies as _resolve_route_dependencies_impl,
|
|
24
|
+
)
|
|
25
|
+
from tigrbl.app.transport import (
|
|
26
|
+
asgi_app as _asgi_app_impl,
|
|
27
|
+
wsgi_app as _wsgi_app_impl,
|
|
28
|
+
)
|
|
29
|
+
from tigrbl.requests.adapters import (
|
|
30
|
+
request_from_asgi as _request_from_asgi_impl,
|
|
31
|
+
request_from_wsgi as _request_from_wsgi_impl,
|
|
32
|
+
)
|
|
33
|
+
from tigrbl.responses import Response
|
|
34
|
+
from tigrbl.runtime.status.exceptions import HTTPException
|
|
35
|
+
from tigrbl.runtime.status.mappings import status
|
|
36
|
+
from tigrbl.transport.httpx import ensure_httpx_sync_transport
|
|
37
|
+
|
|
38
|
+
from ._route import Route
|
|
39
|
+
from ..system.docs.openapi import build_openapi, mount_openapi
|
|
40
|
+
from ..system.docs.openapi.metadata import is_metadata_route as _is_metadata_route_impl
|
|
41
|
+
from ..system.docs.swagger import mount_swagger
|
|
42
|
+
from ..requests import Request
|
|
43
|
+
from ..transport.rest.decorators import (
|
|
44
|
+
delete as rest_delete,
|
|
45
|
+
get as rest_get,
|
|
46
|
+
patch as rest_patch,
|
|
47
|
+
post as rest_post,
|
|
48
|
+
put as rest_put,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
Handler = Callable[..., Any]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@asynccontextmanager
|
|
55
|
+
async def _default_lifespan_context(app: Any):
|
|
56
|
+
yield
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Router:
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
*,
|
|
63
|
+
title: str = "API",
|
|
64
|
+
version: str = "0.1.0",
|
|
65
|
+
description: str | None = None,
|
|
66
|
+
openapi_url: str = "/openapi.json",
|
|
67
|
+
docs_url: str = "/docs",
|
|
68
|
+
debug: bool = False,
|
|
69
|
+
swagger_ui_version: str = "5.31.0",
|
|
70
|
+
prefix: str = "",
|
|
71
|
+
tags: list[str] | None = None,
|
|
72
|
+
dependencies: list[Any] | None = None,
|
|
73
|
+
include_docs: bool = False,
|
|
74
|
+
) -> None:
|
|
75
|
+
self.title = title
|
|
76
|
+
self.version = version
|
|
77
|
+
self.description = description
|
|
78
|
+
self.openapi_url = openapi_url
|
|
79
|
+
self.docs_url = docs_url
|
|
80
|
+
self.debug = debug
|
|
81
|
+
self.swagger_ui_version = swagger_ui_version
|
|
82
|
+
self.prefix = normalize_prefix(prefix)
|
|
83
|
+
self.tags = list(tags or [])
|
|
84
|
+
self.dependencies = list(dependencies or [])
|
|
85
|
+
# Allow dependencies to be replaced at runtime, typically for testing
|
|
86
|
+
# and environment-specific wiring.
|
|
87
|
+
self.dependency_overrides: dict[Callable[..., Any], Callable[..., Any]] = {}
|
|
88
|
+
self.dependency_overrides_provider = self
|
|
89
|
+
self._event_handlers: dict[str, list[Callable[..., Any]]] = {
|
|
90
|
+
"startup": [],
|
|
91
|
+
"shutdown": [],
|
|
92
|
+
}
|
|
93
|
+
self.lifespan_context = self._lifespan_context
|
|
94
|
+
|
|
95
|
+
self.lifespan_context = _default_lifespan_context
|
|
96
|
+
|
|
97
|
+
self._routes: list[Route] = []
|
|
98
|
+
self.routes = self._routes
|
|
99
|
+
|
|
100
|
+
if include_docs:
|
|
101
|
+
self._install_builtin_routes()
|
|
102
|
+
|
|
103
|
+
@asynccontextmanager
|
|
104
|
+
async def _lifespan_context(self, _: Any):
|
|
105
|
+
"""ASGI lifecycle context manager for startup/shutdown hooks."""
|
|
106
|
+
await self.run_event_handlers("startup")
|
|
107
|
+
try:
|
|
108
|
+
yield
|
|
109
|
+
finally:
|
|
110
|
+
await self.run_event_handlers("shutdown")
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def event_handlers(self) -> dict[str, list[Callable[..., Any]]]:
|
|
114
|
+
"""Expose registered startup and shutdown callbacks by event name."""
|
|
115
|
+
return self._event_handlers
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def on_startup(self) -> list[Callable[..., Any]]:
|
|
119
|
+
"""Provide direct access to startup callbacks for lifecycle runners."""
|
|
120
|
+
return self._event_handlers["startup"]
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def on_shutdown(self) -> list[Callable[..., Any]]:
|
|
124
|
+
"""Provide direct access to shutdown callbacks for lifecycle runners."""
|
|
125
|
+
return self._event_handlers["shutdown"]
|
|
126
|
+
|
|
127
|
+
def add_event_handler(
|
|
128
|
+
self,
|
|
129
|
+
event_type: str,
|
|
130
|
+
handler: Callable[..., Any],
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Register a startup or shutdown handler."""
|
|
133
|
+
if event_type not in self._event_handlers:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"Unsupported event type '{event_type}'. "
|
|
136
|
+
f"Expected one of: {tuple(self._event_handlers.keys())}."
|
|
137
|
+
)
|
|
138
|
+
self._event_handlers[event_type].append(handler)
|
|
139
|
+
|
|
140
|
+
def on_event(
|
|
141
|
+
self, event_type: str
|
|
142
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
143
|
+
"""Decorator form of :meth:`add_event_handler`."""
|
|
144
|
+
|
|
145
|
+
def _decorator(handler: Callable[..., Any]) -> Callable[..., Any]:
|
|
146
|
+
self.add_event_handler(event_type, handler)
|
|
147
|
+
return handler
|
|
148
|
+
|
|
149
|
+
return _decorator
|
|
150
|
+
|
|
151
|
+
async def run_event_handlers(self, event_type: str) -> None:
|
|
152
|
+
"""Execute registered handlers for an event type in registration order."""
|
|
153
|
+
if event_type not in self._event_handlers:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
f"Unsupported event type '{event_type}'. "
|
|
156
|
+
f"Expected one of: {tuple(self._event_handlers.keys())}."
|
|
157
|
+
)
|
|
158
|
+
for handler in self._event_handlers[event_type]:
|
|
159
|
+
result = handler()
|
|
160
|
+
if inspect.isawaitable(result):
|
|
161
|
+
await result
|
|
162
|
+
|
|
163
|
+
def _normalize_prefix(self, prefix: str) -> str:
|
|
164
|
+
return normalize_prefix(prefix)
|
|
165
|
+
|
|
166
|
+
def add_api_route(self, path: str, endpoint: Handler, **kwargs: Any) -> None:
|
|
167
|
+
return add_api_route(self, path, endpoint, **kwargs)
|
|
168
|
+
|
|
169
|
+
def _merge_tags(self, tags: list[str] | None) -> list[str] | None:
|
|
170
|
+
return merge_tags(self.tags, tags)
|
|
171
|
+
|
|
172
|
+
def route(
|
|
173
|
+
self, path: str, *, methods: Any, **kwargs: Any
|
|
174
|
+
) -> Callable[[Handler], Handler]:
|
|
175
|
+
return route(self, path, methods=methods, **kwargs)
|
|
176
|
+
|
|
177
|
+
def get(self, path: str, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
178
|
+
return rest_get(self, path, **kwargs)
|
|
179
|
+
|
|
180
|
+
def post(self, path: str, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
181
|
+
return rest_post(self, path, **kwargs)
|
|
182
|
+
|
|
183
|
+
def put(self, path: str, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
184
|
+
return rest_put(self, path, **kwargs)
|
|
185
|
+
|
|
186
|
+
def patch(self, path: str, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
187
|
+
return rest_patch(self, path, **kwargs)
|
|
188
|
+
|
|
189
|
+
def delete(self, path: str, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
190
|
+
return rest_delete(self, path, **kwargs)
|
|
191
|
+
|
|
192
|
+
def include_router(self, other: "Router", **kwargs: Any) -> None:
|
|
193
|
+
return include_router(self, other, **kwargs)
|
|
194
|
+
|
|
195
|
+
def __call__(self, *args: Any, **kwargs: Any):
|
|
196
|
+
return self._router_call(*args, **kwargs)
|
|
197
|
+
|
|
198
|
+
def _router_call(self, *args: Any, **kwargs: Any):
|
|
199
|
+
"""Dispatch entrypoint supporting WSGI and ASGI call conventions.
|
|
200
|
+
|
|
201
|
+
The router is designed to be directly mountable on WSGI *or* ASGI
|
|
202
|
+
servers without additional glue code.
|
|
203
|
+
|
|
204
|
+
Supported invocation forms
|
|
205
|
+
--------------------------
|
|
206
|
+
WSGI (PEP 3333)
|
|
207
|
+
``router(environ: dict, start_response: Callable) -> list[bytes]``
|
|
208
|
+
|
|
209
|
+
ASGI 3 (single callable)
|
|
210
|
+
``router(scope: dict, receive: Callable, send: Callable) -> Awaitable[None]``
|
|
211
|
+
|
|
212
|
+
ASGI 2 (callable factory)
|
|
213
|
+
``router(scope: dict) -> Callable[[receive, send], Awaitable[None]]``
|
|
214
|
+
|
|
215
|
+
The protocol is inferred from positional arguments.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
del kwargs
|
|
219
|
+
if len(args) == 2 and isinstance(args[0], dict) and callable(args[1]):
|
|
220
|
+
return self._wsgi_app(args[0], args[1])
|
|
221
|
+
if len(args) == 1 and isinstance(args[0], dict):
|
|
222
|
+
scope = args[0]
|
|
223
|
+
|
|
224
|
+
async def _asgi2_instance(receive: Callable, send: Callable) -> None:
|
|
225
|
+
await self._asgi_app(scope, receive, send)
|
|
226
|
+
|
|
227
|
+
return _asgi2_instance
|
|
228
|
+
if len(args) == 3 and isinstance(args[0], dict):
|
|
229
|
+
return self._asgi_app(args[0], args[1], args[2])
|
|
230
|
+
raise TypeError("Invalid ASGI/WSGI invocation")
|
|
231
|
+
|
|
232
|
+
def _wsgi_app(
|
|
233
|
+
self, environ: dict[str, Any], start_response: Callable[..., Any]
|
|
234
|
+
) -> list[bytes]:
|
|
235
|
+
return _wsgi_app_impl(self, environ, start_response)
|
|
236
|
+
|
|
237
|
+
async def _asgi_app(
|
|
238
|
+
self, scope: dict[str, Any], receive: Callable, send: Callable
|
|
239
|
+
) -> None:
|
|
240
|
+
await _asgi_app_impl(self, scope, receive, send)
|
|
241
|
+
|
|
242
|
+
def _request_from_wsgi(self, environ: dict[str, Any]) -> Request:
|
|
243
|
+
return _request_from_wsgi_impl(self, environ)
|
|
244
|
+
|
|
245
|
+
def _request_from_asgi(self, scope: dict[str, Any], body: bytes) -> Request:
|
|
246
|
+
return _request_from_asgi_impl(self, scope, body)
|
|
247
|
+
|
|
248
|
+
def _route_match_priority(self, route: Route) -> tuple[int, int, int]:
|
|
249
|
+
return _route_match_priority(route)
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _is_http_response_like(obj: Any) -> bool:
|
|
253
|
+
return (
|
|
254
|
+
hasattr(obj, "status_code")
|
|
255
|
+
and hasattr(obj, "headers")
|
|
256
|
+
and (
|
|
257
|
+
hasattr(obj, "body")
|
|
258
|
+
or hasattr(obj, "body_iterator")
|
|
259
|
+
or hasattr(obj, "render")
|
|
260
|
+
or hasattr(obj, "path")
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
async def dispatch(self, req: Request) -> Response:
|
|
265
|
+
"""Route an incoming request to the best matching handler."""
|
|
266
|
+
|
|
267
|
+
path_matches = [r for r in self._routes if r.pattern.match(req.path)]
|
|
268
|
+
|
|
269
|
+
if req.method.upper() == "OPTIONS" and path_matches:
|
|
270
|
+
return _build_options_response(req, path_matches)
|
|
271
|
+
|
|
272
|
+
candidates = [r for r in path_matches if req.method in r.methods]
|
|
273
|
+
candidates.sort(key=self._route_match_priority)
|
|
274
|
+
for candidate in candidates:
|
|
275
|
+
match = candidate.pattern.match(req.path)
|
|
276
|
+
if not match:
|
|
277
|
+
continue
|
|
278
|
+
req2 = Request(
|
|
279
|
+
method=req.method,
|
|
280
|
+
path=req.path,
|
|
281
|
+
headers=req.headers,
|
|
282
|
+
query=req.query,
|
|
283
|
+
path_params={k: v for k, v in match.groupdict().items()},
|
|
284
|
+
body=req.body,
|
|
285
|
+
script_name=req.script_name,
|
|
286
|
+
app=self,
|
|
287
|
+
state=req.state,
|
|
288
|
+
scope=req.scope,
|
|
289
|
+
)
|
|
290
|
+
return await self.call_handler(candidate, req2)
|
|
291
|
+
|
|
292
|
+
# If the path exists for any method, return a 405 + Allow header.
|
|
293
|
+
if path_matches:
|
|
294
|
+
allowed = {
|
|
295
|
+
method.upper()
|
|
296
|
+
for candidate in path_matches
|
|
297
|
+
for method in getattr(candidate, "methods", ())
|
|
298
|
+
}
|
|
299
|
+
allowed.add("OPTIONS")
|
|
300
|
+
return Response.json(
|
|
301
|
+
{"detail": "Method Not Allowed"},
|
|
302
|
+
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
|
|
303
|
+
headers={"allow": ",".join(sorted(allowed))},
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
return Response.json(
|
|
307
|
+
{"detail": "Not Found"},
|
|
308
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
async def call_handler(self, route: Route, req: Request) -> Response:
|
|
312
|
+
"""Resolve dependencies, invoke the handler, and normalize its output."""
|
|
313
|
+
|
|
314
|
+
dependency_cleanups: list[Callable[[], Any]] = []
|
|
315
|
+
setattr(req.state, "_dependency_cleanups", dependency_cleanups)
|
|
316
|
+
try:
|
|
317
|
+
await self._resolve_route_dependencies(route, req)
|
|
318
|
+
kwargs = await self._resolve_handler_kwargs(route, req)
|
|
319
|
+
out = route.handler(**kwargs)
|
|
320
|
+
if inspect.isawaitable(out):
|
|
321
|
+
out = await out
|
|
322
|
+
except HTTPException as he:
|
|
323
|
+
return Response.json(
|
|
324
|
+
{"detail": he.detail},
|
|
325
|
+
status_code=he.status_code,
|
|
326
|
+
headers=he.headers,
|
|
327
|
+
)
|
|
328
|
+
except Exception as exc:
|
|
329
|
+
# Normalize exception objects that provide ``status_code`` + ``detail``
|
|
330
|
+
# into HTTP JSON responses.
|
|
331
|
+
if hasattr(exc, "status_code") and hasattr(exc, "detail"):
|
|
332
|
+
return Response.json(
|
|
333
|
+
{"detail": getattr(exc, "detail")},
|
|
334
|
+
status_code=getattr(exc, "status_code"),
|
|
335
|
+
headers=getattr(exc, "headers", None),
|
|
336
|
+
)
|
|
337
|
+
raise
|
|
338
|
+
finally:
|
|
339
|
+
for cleanup in reversed(dependency_cleanups):
|
|
340
|
+
try:
|
|
341
|
+
result = cleanup()
|
|
342
|
+
if inspect.isawaitable(result):
|
|
343
|
+
await result
|
|
344
|
+
except Exception:
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
if isinstance(out, Response):
|
|
348
|
+
return out
|
|
349
|
+
|
|
350
|
+
if self._is_http_response_like(out):
|
|
351
|
+
body = bytes(getattr(out, "body", b"") or b"")
|
|
352
|
+
if not body and hasattr(out, "body_iterator"):
|
|
353
|
+
chunks: list[bytes] = []
|
|
354
|
+
body_iter = getattr(out, "body_iterator")
|
|
355
|
+
if body_iter is not None:
|
|
356
|
+
if hasattr(body_iter, "__aiter__"):
|
|
357
|
+
async for chunk in body_iter:
|
|
358
|
+
chunks.append(
|
|
359
|
+
chunk.encode("utf-8")
|
|
360
|
+
if isinstance(chunk, str)
|
|
361
|
+
else bytes(chunk)
|
|
362
|
+
)
|
|
363
|
+
else:
|
|
364
|
+
for chunk in body_iter:
|
|
365
|
+
chunks.append(
|
|
366
|
+
chunk.encode("utf-8")
|
|
367
|
+
if isinstance(chunk, str)
|
|
368
|
+
else bytes(chunk)
|
|
369
|
+
)
|
|
370
|
+
body = b"".join(chunks)
|
|
371
|
+
if not body and hasattr(out, "path"):
|
|
372
|
+
path = getattr(out, "path")
|
|
373
|
+
if isinstance(path, str):
|
|
374
|
+
with open(path, "rb") as fp:
|
|
375
|
+
body = fp.read()
|
|
376
|
+
raw_headers = getattr(out, "headers", {})
|
|
377
|
+
if hasattr(raw_headers, "items"):
|
|
378
|
+
headers = list(raw_headers.items())
|
|
379
|
+
else:
|
|
380
|
+
headers = list(raw_headers)
|
|
381
|
+
media_type = getattr(out, "media_type", None)
|
|
382
|
+
if media_type and not any(k.lower() == "content-type" for k, _ in headers):
|
|
383
|
+
headers.append(("content-type", media_type))
|
|
384
|
+
return Response(
|
|
385
|
+
status_code=getattr(out, "status_code", 200),
|
|
386
|
+
headers=headers,
|
|
387
|
+
body=body,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
code = route.status_code if route.status_code is not None else 200
|
|
391
|
+
if out is None:
|
|
392
|
+
if code == 204:
|
|
393
|
+
return Response(
|
|
394
|
+
status_code=204,
|
|
395
|
+
headers=[("content-length", "0")],
|
|
396
|
+
body=b"",
|
|
397
|
+
)
|
|
398
|
+
return Response.json(None, status_code=code)
|
|
399
|
+
if isinstance(out, (str, bytes, bytearray)):
|
|
400
|
+
return Response(
|
|
401
|
+
status_code=code,
|
|
402
|
+
headers=[("content-type", "application/octet-stream")],
|
|
403
|
+
body=bytes(out),
|
|
404
|
+
)
|
|
405
|
+
return Response.json(out, status_code=code)
|
|
406
|
+
|
|
407
|
+
async def _dispatch(self, req: Request):
|
|
408
|
+
return await self.dispatch(req)
|
|
409
|
+
|
|
410
|
+
async def _call_handler(self, route: Route, req: Request):
|
|
411
|
+
return await self.call_handler(route, req)
|
|
412
|
+
|
|
413
|
+
async def _resolve_route_dependencies(self, route: Route, req: Request) -> None:
|
|
414
|
+
return await _resolve_route_dependencies_impl(self, route, req)
|
|
415
|
+
|
|
416
|
+
def _is_metadata_route(self, route: Route) -> bool:
|
|
417
|
+
return _is_metadata_route_impl(self, route)
|
|
418
|
+
|
|
419
|
+
async def _resolve_handler_kwargs(
|
|
420
|
+
self, route: Route, req: Request
|
|
421
|
+
) -> dict[str, Any]:
|
|
422
|
+
return await _resolve_handler_kwargs_impl(self, route, req)
|
|
423
|
+
|
|
424
|
+
async def _invoke_dependency(self, dep: Callable[..., Any], req: Request) -> Any:
|
|
425
|
+
return await _invoke_dependency_impl(self, dep, req)
|
|
426
|
+
|
|
427
|
+
def openapi(self) -> dict[str, Any]:
|
|
428
|
+
return build_openapi(self)
|
|
429
|
+
|
|
430
|
+
def _install_builtin_routes(self) -> None:
|
|
431
|
+
mount_openapi(self, path=self.openapi_url)
|
|
432
|
+
mount_swagger(self, path=self.docs_url)
|
|
433
|
+
|
|
434
|
+
def _swagger_ui_html(self, request: Request) -> str:
|
|
435
|
+
docs_route = next(
|
|
436
|
+
(route for route in self._routes if route.name == "__docs__"), None
|
|
437
|
+
)
|
|
438
|
+
if docs_route is None:
|
|
439
|
+
mount_swagger(self, path=self.docs_url)
|
|
440
|
+
docs_route = next(
|
|
441
|
+
(route for route in self._routes if route.name == "__docs__"), None
|
|
442
|
+
)
|
|
443
|
+
if docs_route is None:
|
|
444
|
+
raise RuntimeError("Unable to resolve mounted swagger docs route.")
|
|
445
|
+
|
|
446
|
+
response = docs_route.handler(request)
|
|
447
|
+
body = getattr(response, "body", b"")
|
|
448
|
+
if isinstance(body, bytes):
|
|
449
|
+
return body.decode("utf-8")
|
|
450
|
+
return str(body)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _route_match_priority(route: Route) -> tuple[int, int, int]:
|
|
454
|
+
is_metadata = int(getattr(route, "name", "") in {"__openapi__", "__docs__"})
|
|
455
|
+
dynamic_segments = route.path_template.count("{")
|
|
456
|
+
path_length = -len(route.path_template)
|
|
457
|
+
return (-is_metadata, dynamic_segments, path_length)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _build_options_response(req: Request, routes: list[Route]) -> Response:
|
|
461
|
+
"""Build an automatic OPTIONS response for a matched path."""
|
|
462
|
+
allowed_methods = {
|
|
463
|
+
method.upper() for route in routes for method in getattr(route, "methods", ())
|
|
464
|
+
}
|
|
465
|
+
allowed_methods.add("OPTIONS")
|
|
466
|
+
allow_value = ",".join(sorted(allowed_methods))
|
|
467
|
+
|
|
468
|
+
headers: dict[str, str] = {"allow": allow_value}
|
|
469
|
+
|
|
470
|
+
origin = req.headers.get("origin")
|
|
471
|
+
if origin:
|
|
472
|
+
headers["access-control-allow-origin"] = origin
|
|
473
|
+
headers["vary"] = "origin"
|
|
474
|
+
|
|
475
|
+
request_headers = req.headers.get("access-control-request-headers")
|
|
476
|
+
if request_headers:
|
|
477
|
+
headers["access-control-allow-headers"] = request_headers
|
|
478
|
+
|
|
479
|
+
if origin and request_headers:
|
|
480
|
+
headers["vary"] = "origin,access-control-request-headers"
|
|
481
|
+
|
|
482
|
+
headers["access-control-allow-methods"] = allow_value
|
|
483
|
+
|
|
484
|
+
return Response(status_code=204, headers=headers, body=b"")
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
ensure_httpx_sync_transport()
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
APIRouter = Router
|
|
@@ -151,20 +151,4 @@ def wsgi_app(
|
|
|
151
151
|
return [resp.body]
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
del kwargs
|
|
156
|
-
if len(args) == 2 and isinstance(args[0], dict) and callable(args[1]):
|
|
157
|
-
return wsgi_app(router, args[0], args[1])
|
|
158
|
-
if len(args) == 1 and isinstance(args[0], dict):
|
|
159
|
-
scope = args[0]
|
|
160
|
-
|
|
161
|
-
async def _asgi2_instance(receive: Callable, send: Callable) -> None:
|
|
162
|
-
await asgi_app(router, scope, receive, send)
|
|
163
|
-
|
|
164
|
-
return _asgi2_instance
|
|
165
|
-
if len(args) == 3 and isinstance(args[0], dict):
|
|
166
|
-
return asgi_app(router, args[0], args[1], args[2])
|
|
167
|
-
raise TypeError("Invalid ASGI/WSGI invocation")
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
__all__ = ["asgi_app", "router_call", "wsgi_app"]
|
|
154
|
+
__all__ = ["asgi_app", "wsgi_app"]
|
|
@@ -410,7 +410,39 @@ def build_jsonrpc_router(
|
|
|
410
410
|
err = _err(-32600, "Invalid Request", None)
|
|
411
411
|
return JSONResponse(content=err)
|
|
412
412
|
|
|
413
|
+
async def _options_endpoint(request: Request):
|
|
414
|
+
allow = "OPTIONS,POST"
|
|
415
|
+
headers: Dict[str, str] = {
|
|
416
|
+
"allow": allow,
|
|
417
|
+
"access-control-allow-methods": allow,
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
origin = request.headers.get("origin")
|
|
421
|
+
if origin:
|
|
422
|
+
headers["access-control-allow-origin"] = origin
|
|
423
|
+
headers["vary"] = "origin"
|
|
424
|
+
|
|
425
|
+
req_headers = request.headers.get("access-control-request-headers")
|
|
426
|
+
if req_headers:
|
|
427
|
+
headers["access-control-allow-headers"] = req_headers
|
|
428
|
+
headers["vary"] = (
|
|
429
|
+
"origin,access-control-request-headers"
|
|
430
|
+
if origin
|
|
431
|
+
else "access-control-request-headers"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
return Response(status_code=204, headers=headers)
|
|
435
|
+
|
|
413
436
|
# Attach a single JSON-RPC POST route. Mount prefix controls final path.
|
|
437
|
+
router.add_api_route(
|
|
438
|
+
path="",
|
|
439
|
+
endpoint=_options_endpoint,
|
|
440
|
+
methods=["OPTIONS"],
|
|
441
|
+
name="jsonrpc_options",
|
|
442
|
+
tags=list(tags) if tags else None,
|
|
443
|
+
include_in_schema=False,
|
|
444
|
+
)
|
|
445
|
+
|
|
414
446
|
router.add_api_route(
|
|
415
447
|
path="",
|
|
416
448
|
endpoint=_endpoint,
|