tigrbl 0.3.0.dev4__tar.gz → 0.3.1__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.0.dev4 → tigrbl-0.3.1}/PKG-INFO +19 -4
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/README.md +9 -1
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/pyproject.toml +20 -2
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/_api.py +26 -1
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/tigrbl_api.py +6 -1
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/_app.py +26 -1
- tigrbl-0.3.1/tigrbl/app/_model_registry.py +41 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/tigrbl_app.py +6 -1
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/collection.py +24 -3
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/common.py +4 -0
- tigrbl-0.3.1/tigrbl/bindings/rest/io_headers.py +49 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/member.py +19 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/router.py +4 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/routing.py +21 -1
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/io_spec.py +3 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/__init__.py +19 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/_engine.py +14 -0
- tigrbl-0.3.1/tigrbl/engine/capabilities.py +29 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/decorators.py +3 -1
- tigrbl-0.3.1/tigrbl/engine/docs/PLUGINS.md +49 -0
- tigrbl-0.3.1/tigrbl/engine/engine_spec.py +355 -0
- tigrbl-0.3.1/tigrbl/engine/plugins.py +52 -0
- tigrbl-0.3.1/tigrbl/engine/registry.py +36 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/upsertable.py +7 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/shortcuts.py +31 -4
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/__init__.py +2 -0
- tigrbl-0.3.1/tigrbl/runtime/atoms/response/headers_from_payload.py +57 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/kernel.py +27 -11
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/opview.py +5 -3
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/collect.py +26 -2
- tigrbl-0.3.1/tigrbl/session/README.md +14 -0
- tigrbl-0.3.1/tigrbl/session/__init__.py +28 -0
- tigrbl-0.3.1/tigrbl/session/abc.py +76 -0
- tigrbl-0.3.1/tigrbl/session/base.py +151 -0
- tigrbl-0.3.1/tigrbl/session/decorators.py +43 -0
- tigrbl-0.3.1/tigrbl/session/default.py +118 -0
- tigrbl-0.3.1/tigrbl/session/shortcuts.py +50 -0
- tigrbl-0.3.1/tigrbl/session/spec.py +112 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/__init__.py +2 -1
- tigrbl-0.3.1/tigrbl/system/uvicorn.py +60 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/_base.py +28 -5
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/__init__.py +3 -7
- tigrbl-0.3.1/tigrbl/types/uuid.py +55 -0
- tigrbl-0.3.0.dev4/tigrbl/engine/engine_spec.py +0 -261
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/LICENSE +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/README.md +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/api_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/api/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/app_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/app/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/api/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/api/common.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/api/include.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/api/resource_proxy.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/api/rpc.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/columns.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/builder.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/ctx.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/identifiers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/namespaces.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/handlers/steps.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/hooks.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/model.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/model_helpers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/model_registry.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/attach.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/fastapi.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/helpers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rest/io.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/rpc.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/schemas/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/schemas/builder.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/schemas/defaults.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/bindings/schemas/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/README.md +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/_column.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/column_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/field_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/core.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/jsonhints.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/planning.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/infer/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/column/storage_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/config/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/config/constants.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/config/defaults.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/config/resolver.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/bulk.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/db.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/enum.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/filters.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/model.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/helpers/normalize.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/core/crud/ops.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/ddl/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/decorators.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/fastapi.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/favicon.svg +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/jinja.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/pydantic.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/sqlalchemy.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/deps/starlette.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/bind.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/builders.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/resolver.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/engine/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/_hook.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/decorators.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/hook_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/hook/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/_op.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/canonical.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/decorators.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/model_registry.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/resolver.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/op/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/_RowBound.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/bootstrappable.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/bound.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/edges.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/fields.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/hierarchy.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/key_digest.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/lifecycle.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/locks.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/markers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/operations.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/ownable.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/principals.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/tenant_bound.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/mixins/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/_base.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/audit.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/client.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/group.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/org.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/rbac.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/status.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/tenant.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/orm/tables/user.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/README.md +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/bind.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/decorators.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/resolver.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/response/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/rest/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/README.md +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/emit/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/emit/paired_post.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/emit/paired_pre.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/emit/readtime_alias.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/out/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/out/masking.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/refresh/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/refresh/demand.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/resolve/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/resolve/assemble.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/resolve/paired_gen.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/negotiate.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/negotiation.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/render.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/renderer.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/template.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/response/templates.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/schema/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/schema/collect_in.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/schema/collect_out.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/storage/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/storage/to_stored.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/wire/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/wire/build_in.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/wire/build_out.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/wire/dump.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/atoms/wire/validate_in.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/context.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/errors/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/errors/converters.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/errors/exceptions.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/errors/mappings.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/errors/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/events.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/executor/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/executor/guards.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/executor/helpers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/executor/invoke.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/executor/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/labels.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/ordering.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/system.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/runtime/trace.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/_schema.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/build_schema.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/cache.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/compat.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/extras.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/helpers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/list_params.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/builder/strip_parent_fields.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/decorators.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/get_schema.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/schema_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/types.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/schema/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/specs.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/compat.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/healthz.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/hookz.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/kernelz.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/methodz.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/router.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/system/diagnostics/utils.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/_table.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/mro_collect.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/shortcuts.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/table/table_spec.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/jsonrpc/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/jsonrpc/dispatcher.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/jsonrpc/helpers.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/jsonrpc/models.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/rest/__init__.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/transport/rest/aggregator.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/allow_anon_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/authn_abc.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/nested_path_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/op.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/op_config_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/op_verb_alias_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/request_extras_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/response_extras_provider.py +0 -0
- {tigrbl-0.3.0.dev4 → tigrbl-0.3.1}/tigrbl/types/table_config_provider.py +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrbl
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Automatic API generation tools by Swarmauri.
|
|
5
|
-
License: Apache-2.0
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: tigrbl,sdk,standards
|
|
6
8
|
Author: Swarmauri Team
|
|
7
9
|
Author-email: team@swarmauri.com
|
|
8
10
|
Requires-Python: >=3.10,<3.13
|
|
@@ -11,8 +13,12 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
11
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
19
|
Provides-Extra: postgres
|
|
15
20
|
Provides-Extra: templates
|
|
21
|
+
Provides-Extra: tests
|
|
16
22
|
Requires-Dist: aiosqlite (>=0.19.0)
|
|
17
23
|
Requires-Dist: asyncpg (>=0.30.0) ; extra == "postgres"
|
|
18
24
|
Requires-Dist: fastapi (>=0.100.0)
|
|
@@ -25,10 +31,11 @@ Requires-Dist: sqlalchemy (>=2.0)
|
|
|
25
31
|
Requires-Dist: swarmauri_base
|
|
26
32
|
Requires-Dist: swarmauri_core
|
|
27
33
|
Requires-Dist: swarmauri_standard
|
|
34
|
+
Requires-Dist: tigrbl-tests ; extra == "tests"
|
|
28
35
|
Requires-Dist: uvicorn
|
|
29
36
|
Description-Content-Type: text/markdown
|
|
30
37
|
|
|
31
|
-

|
|
32
39
|
|
|
33
40
|
<p align="center">
|
|
34
41
|
<a href="https://pypi.org/project/tigrbl/">
|
|
@@ -48,6 +55,14 @@ Description-Content-Type: text/markdown
|
|
|
48
55
|
# Tigrbl 🐅🐂
|
|
49
56
|
A high-leverage meta-framework that turns plain SQLAlchemy models into a fully-featured REST+RPC surface with near-zero boilerplate. 🚀
|
|
50
57
|
|
|
58
|
+
## Features ✨
|
|
59
|
+
|
|
60
|
+
- ⚡ Zero-boilerplate CRUD for SQLAlchemy models
|
|
61
|
+
- 🔌 Unified REST and RPC endpoints from a single definition
|
|
62
|
+
- 🪝 Hookable phase system for deep customization
|
|
63
|
+
- 🧩 Pluggable engine and provider abstractions
|
|
64
|
+
- 🚀 Built on FastAPI and Pydantic for modern Python web apps
|
|
65
|
+
|
|
51
66
|
## Terminology 📚
|
|
52
67
|
|
|
53
68
|
- **Tenant** 🏢 – a namespace used to group related resources.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://pypi.org/project/tigrbl/">
|
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
# Tigrbl 🐅🐂
|
|
19
19
|
A high-leverage meta-framework that turns plain SQLAlchemy models into a fully-featured REST+RPC surface with near-zero boilerplate. 🚀
|
|
20
20
|
|
|
21
|
+
## Features ✨
|
|
22
|
+
|
|
23
|
+
- ⚡ Zero-boilerplate CRUD for SQLAlchemy models
|
|
24
|
+
- 🔌 Unified REST and RPC endpoints from a single definition
|
|
25
|
+
- 🪝 Hookable phase system for deep customization
|
|
26
|
+
- 🧩 Pluggable engine and provider abstractions
|
|
27
|
+
- 🚀 Built on FastAPI and Pydantic for modern Python web apps
|
|
28
|
+
|
|
21
29
|
## Terminology 📚
|
|
22
30
|
|
|
23
31
|
- **Tenant** 🏢 – a namespace used to group related resources.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tigrbl"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Automatic API generation tools by Swarmauri."
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -12,6 +12,9 @@ classifiers = [
|
|
|
12
12
|
"Programming Language :: Python :: 3.10",
|
|
13
13
|
"Programming Language :: Python :: 3.11",
|
|
14
14
|
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
18
|
]
|
|
16
19
|
authors = [{ name = "Swarmauri Team", email = "team@swarmauri.com" }]
|
|
17
20
|
dependencies = [
|
|
@@ -26,6 +29,11 @@ dependencies = [
|
|
|
26
29
|
"greenlet>=3.2.3",
|
|
27
30
|
"uvicorn",
|
|
28
31
|
]
|
|
32
|
+
keywords = [
|
|
33
|
+
'tigrbl',
|
|
34
|
+
'sdk',
|
|
35
|
+
'standards',
|
|
36
|
+
]
|
|
29
37
|
|
|
30
38
|
[project.optional-dependencies]
|
|
31
39
|
postgres = [
|
|
@@ -35,14 +43,19 @@ postgres = [
|
|
|
35
43
|
templates = [
|
|
36
44
|
"jinja2>=3.1.4",
|
|
37
45
|
]
|
|
46
|
+
tests = [
|
|
47
|
+
"tigrbl-tests",
|
|
48
|
+
]
|
|
38
49
|
|
|
39
50
|
[tool.uv.sources]
|
|
40
51
|
swarmauri_core = { workspace = true }
|
|
41
52
|
swarmauri_base = { workspace = true }
|
|
42
53
|
swarmauri_standard = { workspace = true }
|
|
54
|
+
tigrbl-tests = { workspace = true }
|
|
43
55
|
|
|
44
56
|
[tool.pytest.ini_options]
|
|
45
57
|
norecursedirs = ["combined", "scripts"]
|
|
58
|
+
testpaths = ["../tigrbl_tests/tests"]
|
|
46
59
|
markers = [
|
|
47
60
|
"test: standard test",
|
|
48
61
|
"unit: Unit tests",
|
|
@@ -65,6 +78,11 @@ asyncio_default_fixture_loop_scope = "function"
|
|
|
65
78
|
requires = ["poetry-core>=1.0.0"]
|
|
66
79
|
build-backend = "poetry.core.masonry.api"
|
|
67
80
|
|
|
81
|
+
[tool.poetry]
|
|
82
|
+
packages = [
|
|
83
|
+
{ include = "tigrbl" },
|
|
84
|
+
]
|
|
85
|
+
|
|
68
86
|
[dependency-groups]
|
|
69
87
|
dev = [
|
|
70
88
|
"pytest>=8.0",
|
|
@@ -79,4 +97,4 @@ dev = [
|
|
|
79
97
|
"pytest-benchmark>=4.0.0",
|
|
80
98
|
"asyncpg>=0.30.0",
|
|
81
99
|
"psycopg2-binary>=2.9.9",
|
|
82
|
-
]
|
|
100
|
+
]
|
|
@@ -6,7 +6,9 @@ from types import SimpleNamespace
|
|
|
6
6
|
from ..deps.fastapi import APIRouter as ApiRouter
|
|
7
7
|
from ..engine.engine_spec import EngineCfg
|
|
8
8
|
from ..engine import install_from_objects
|
|
9
|
+
from ..ddl import initialize as _ddl_initialize
|
|
9
10
|
from ..engine import resolver as _resolver
|
|
11
|
+
from ..app._model_registry import initialize_model_registry
|
|
10
12
|
from .api_spec import APISpec
|
|
11
13
|
|
|
12
14
|
|
|
@@ -41,7 +43,7 @@ class Api(APISpec, ApiRouter):
|
|
|
41
43
|
self.deps = tuple(getattr(self, "DEPS", ()))
|
|
42
44
|
self.response = getattr(self, "RESPONSE", None)
|
|
43
45
|
# ``models`` is expected to be a dict at runtime for registry lookups.
|
|
44
|
-
self.models
|
|
46
|
+
self.models = initialize_model_registry(getattr(self, "MODELS", ()))
|
|
45
47
|
|
|
46
48
|
ApiRouter.__init__(
|
|
47
49
|
self,
|
|
@@ -70,3 +72,26 @@ class Api(APISpec, ApiRouter):
|
|
|
70
72
|
install_from_objects(app=self, api=a, models=models)
|
|
71
73
|
else:
|
|
72
74
|
install_from_objects(app=self, api=None, models=models)
|
|
75
|
+
|
|
76
|
+
def _collect_tables(self) -> list[Any]:
|
|
77
|
+
seen = set()
|
|
78
|
+
tables = []
|
|
79
|
+
for model in self.models.values():
|
|
80
|
+
if not hasattr(model, "__table__"):
|
|
81
|
+
try: # pragma: no cover - defensive remap
|
|
82
|
+
from ..table import Base
|
|
83
|
+
from ..table._base import _materialize_colspecs_to_sqla
|
|
84
|
+
|
|
85
|
+
_materialize_colspecs_to_sqla(model)
|
|
86
|
+
Base.registry.map_declaratively(model)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
table = getattr(model, "__table__", None)
|
|
90
|
+
if table is not None and not table.columns:
|
|
91
|
+
continue
|
|
92
|
+
if table is not None and table not in seen:
|
|
93
|
+
seen.add(table)
|
|
94
|
+
tables.append(table)
|
|
95
|
+
return tables
|
|
96
|
+
|
|
97
|
+
initialize = _ddl_initialize
|
|
@@ -32,6 +32,7 @@ from ..bindings.rest import build_router_and_attach as _build_router_and_attach
|
|
|
32
32
|
from ..transport import mount_jsonrpc as _mount_jsonrpc
|
|
33
33
|
from ..system import mount_diagnostics as _mount_diagnostics
|
|
34
34
|
from ..op import get_registry, OpSpec
|
|
35
|
+
from ..app._model_registry import initialize_model_registry
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class TigrblApi(_Api):
|
|
@@ -73,7 +74,7 @@ class TigrblApi(_Api):
|
|
|
73
74
|
self.system_prefix = system_prefix
|
|
74
75
|
|
|
75
76
|
# public containers (mirrors used by bindings.api)
|
|
76
|
-
self.models
|
|
77
|
+
self.models = initialize_model_registry(getattr(self, "MODELS", ()))
|
|
77
78
|
self.schemas = SimpleNamespace()
|
|
78
79
|
self.handlers = SimpleNamespace()
|
|
79
80
|
self.hooks = SimpleNamespace()
|
|
@@ -231,6 +232,8 @@ class TigrblApi(_Api):
|
|
|
231
232
|
) -> None:
|
|
232
233
|
if authn is not None:
|
|
233
234
|
self._authn = authn
|
|
235
|
+
if allow_anon is None:
|
|
236
|
+
allow_anon = False
|
|
234
237
|
if allow_anon is not None:
|
|
235
238
|
self._allow_anon = bool(allow_anon)
|
|
236
239
|
if authorize is not None:
|
|
@@ -270,6 +273,8 @@ class TigrblApi(_Api):
|
|
|
270
273
|
tables = []
|
|
271
274
|
for m in self.models.values():
|
|
272
275
|
t = getattr(m, "__table__", None)
|
|
276
|
+
if t is not None and not t.columns:
|
|
277
|
+
continue
|
|
273
278
|
if t is not None and t not in seen:
|
|
274
279
|
seen.add(t)
|
|
275
280
|
tables.append(t)
|
|
@@ -6,6 +6,8 @@ from ..deps.fastapi import FastAPI
|
|
|
6
6
|
from ..engine.engine_spec import EngineCfg
|
|
7
7
|
from ..engine import resolver as _resolver
|
|
8
8
|
from ..engine import install_from_objects
|
|
9
|
+
from ..ddl import initialize as _ddl_initialize
|
|
10
|
+
from ._model_registry import initialize_model_registry
|
|
9
11
|
from .app_spec import AppSpec
|
|
10
12
|
|
|
11
13
|
|
|
@@ -23,7 +25,7 @@ class App(AppSpec, FastAPI):
|
|
|
23
25
|
self.ops = tuple(getattr(self, "OPS", ()))
|
|
24
26
|
# Runtime registries use mutable containers (dict/namespace), but the
|
|
25
27
|
# dataclass fields expect sequences. Storing a dict here satisfies both.
|
|
26
|
-
self.models =
|
|
28
|
+
self.models = initialize_model_registry(getattr(self, "MODELS", ()))
|
|
27
29
|
self.schemas = tuple(getattr(self, "SCHEMAS", ()))
|
|
28
30
|
self.hooks = tuple(getattr(self, "HOOKS", ()))
|
|
29
31
|
self.security_deps = tuple(getattr(self, "SECURITY_DEPS", ()))
|
|
@@ -59,3 +61,26 @@ class App(AppSpec, FastAPI):
|
|
|
59
61
|
install_from_objects(app=self, api=a, models=models)
|
|
60
62
|
else:
|
|
61
63
|
install_from_objects(app=self, api=None, models=models)
|
|
64
|
+
|
|
65
|
+
def _collect_tables(self) -> list[Any]:
|
|
66
|
+
seen = set()
|
|
67
|
+
tables = []
|
|
68
|
+
for model in self.models.values():
|
|
69
|
+
if not hasattr(model, "__table__"):
|
|
70
|
+
try: # pragma: no cover - defensive remap
|
|
71
|
+
from ..table import Base
|
|
72
|
+
from ..table._base import _materialize_colspecs_to_sqla
|
|
73
|
+
|
|
74
|
+
_materialize_colspecs_to_sqla(model)
|
|
75
|
+
Base.registry.map_declaratively(model)
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
table = getattr(model, "__table__", None)
|
|
79
|
+
if table is not None and not table.columns:
|
|
80
|
+
continue
|
|
81
|
+
if table is not None and table not in seen:
|
|
82
|
+
seen.add(table)
|
|
83
|
+
tables.append(table)
|
|
84
|
+
return tables
|
|
85
|
+
|
|
86
|
+
initialize = _ddl_initialize
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Utilities for initializing model registries on App and API facades."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Iterable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def initialize_model_registry(models: Iterable[Any]) -> dict[str, Any]:
|
|
9
|
+
"""Build the default ``models`` mapping for an App or Api instance.
|
|
10
|
+
|
|
11
|
+
``defineAppSpec``/``defineApiSpec`` allow authors to declare default models
|
|
12
|
+
using bare model classes or ``("alias", Model)`` tuples. Runtime facades,
|
|
13
|
+
however, expect ``self.models`` to be a dictionary keyed by model name so
|
|
14
|
+
that lookups like ``app.models["Widget"]`` Just Work.
|
|
15
|
+
|
|
16
|
+
This helper normalizes the declared sequence into that dictionary shape and
|
|
17
|
+
preserves declaration order. When an alias is provided we register both the
|
|
18
|
+
alias and the model's ``__name__`` so either lookup style succeeds.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
registry: dict[str, Any] = {}
|
|
22
|
+
|
|
23
|
+
for entry in models or ():
|
|
24
|
+
# Support ``("Alias", Model)`` declarations in addition to bare models.
|
|
25
|
+
if isinstance(entry, tuple) and len(entry) == 2 and isinstance(entry[0], str):
|
|
26
|
+
alias, model = entry
|
|
27
|
+
registry[alias] = model
|
|
28
|
+
model_name = getattr(model, "__name__", alias)
|
|
29
|
+
registry.setdefault(model_name, model)
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
model = entry
|
|
33
|
+
model_name = getattr(model, "__name__", None)
|
|
34
|
+
if model_name is None:
|
|
35
|
+
model_name = str(model)
|
|
36
|
+
registry[model_name] = model
|
|
37
|
+
|
|
38
|
+
return registry
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = ["initialize_model_registry"]
|
|
@@ -32,6 +32,7 @@ from ..bindings.rest import build_router_and_attach as _build_router_and_attach
|
|
|
32
32
|
from ..transport import mount_jsonrpc as _mount_jsonrpc
|
|
33
33
|
from ..system import mount_diagnostics as _mount_diagnostics
|
|
34
34
|
from ..op import get_registry, OpSpec
|
|
35
|
+
from ._model_registry import initialize_model_registry
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
# optional compat: legacy transactional decorator
|
|
@@ -93,7 +94,7 @@ class TigrblApp(_App):
|
|
|
93
94
|
self.system_prefix = system_prefix
|
|
94
95
|
|
|
95
96
|
# public containers (mirrors used by bindings.api)
|
|
96
|
-
self.models
|
|
97
|
+
self.models = initialize_model_registry(getattr(self, "MODELS", ()))
|
|
97
98
|
self.schemas = SimpleNamespace()
|
|
98
99
|
self.handlers = SimpleNamespace()
|
|
99
100
|
self.hooks = SimpleNamespace()
|
|
@@ -262,6 +263,8 @@ class TigrblApp(_App):
|
|
|
262
263
|
) -> None:
|
|
263
264
|
if authn is not None:
|
|
264
265
|
self._authn = authn
|
|
266
|
+
if allow_anon is None:
|
|
267
|
+
allow_anon = False
|
|
265
268
|
if allow_anon is not None:
|
|
266
269
|
self._allow_anon = bool(allow_anon)
|
|
267
270
|
if authorize is not None:
|
|
@@ -301,6 +304,8 @@ class TigrblApp(_App):
|
|
|
301
304
|
tables = []
|
|
302
305
|
for m in self.models.values():
|
|
303
306
|
t = getattr(m, "__table__", None)
|
|
307
|
+
if t is not None and not t.columns:
|
|
308
|
+
continue
|
|
304
309
|
if t is not None and t not in seen:
|
|
305
310
|
seen.add(t)
|
|
306
311
|
tables.append(t)
|
|
@@ -35,6 +35,8 @@ from .common import (
|
|
|
35
35
|
_status_for,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
+
from .io_headers import _make_header_dep
|
|
39
|
+
|
|
38
40
|
from ...runtime.executor.types import _Ctx
|
|
39
41
|
|
|
40
42
|
|
|
@@ -103,6 +105,7 @@ def _make_collection_endpoint(
|
|
|
103
105
|
) -> Callable[..., Awaitable[Any]]:
|
|
104
106
|
alias, target, nested_vars = sp.alias, sp.target, list(nested_vars or [])
|
|
105
107
|
status_code = _status_for(sp)
|
|
108
|
+
hdr_dep = _make_header_dep(model, alias)
|
|
106
109
|
|
|
107
110
|
if target in {"list", "clear"}:
|
|
108
111
|
list_dep = _make_list_query_dep(model, alias) if target == "list" else None
|
|
@@ -110,6 +113,7 @@ def _make_collection_endpoint(
|
|
|
110
113
|
async def _endpoint(
|
|
111
114
|
request: Request,
|
|
112
115
|
db: Any = Depends(db_dep),
|
|
116
|
+
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
113
117
|
q: Mapping[str, Any] | None = None,
|
|
114
118
|
**kw: Any,
|
|
115
119
|
):
|
|
@@ -120,6 +124,8 @@ def _make_collection_endpoint(
|
|
|
120
124
|
payload = _validate_query(model, alias, target, query)
|
|
121
125
|
else:
|
|
122
126
|
payload = dict(parent_kw)
|
|
127
|
+
if isinstance(h, Mapping):
|
|
128
|
+
payload = {**payload, **dict(h)}
|
|
123
129
|
ctx = _ctx(model, alias, target, request, db, payload, parent_kw, api)
|
|
124
130
|
ctx["response_serializer"] = lambda r: _serialize_output(
|
|
125
131
|
model, alias, target, sp, r
|
|
@@ -154,16 +160,20 @@ def _make_collection_endpoint(
|
|
|
154
160
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
155
161
|
annotation=Annotated[Any, Depends(db_dep)],
|
|
156
162
|
),
|
|
163
|
+
inspect.Parameter(
|
|
164
|
+
"h",
|
|
165
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
166
|
+
annotation=Annotated[Mapping[str, Any], Depends(hdr_dep)],
|
|
167
|
+
),
|
|
157
168
|
]
|
|
158
169
|
)
|
|
159
170
|
if target == "list":
|
|
160
|
-
params.
|
|
161
|
-
len(nested_vars) + 1,
|
|
171
|
+
params.append(
|
|
162
172
|
inspect.Parameter(
|
|
163
173
|
"q",
|
|
164
174
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
165
175
|
annotation=Annotated[Mapping[str, Any], Depends(list_dep)],
|
|
166
|
-
)
|
|
176
|
+
)
|
|
167
177
|
)
|
|
168
178
|
_endpoint.__signature__ = inspect.Signature(params)
|
|
169
179
|
else:
|
|
@@ -192,12 +202,18 @@ def _make_collection_endpoint(
|
|
|
192
202
|
async def _endpoint(
|
|
193
203
|
request: Request,
|
|
194
204
|
db: Any = Depends(db_dep),
|
|
205
|
+
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
195
206
|
body=Body(...),
|
|
196
207
|
**kw: Any,
|
|
197
208
|
):
|
|
198
209
|
parent_kw = {k: kw[k] for k in nested_vars if k in kw}
|
|
199
210
|
_coerce_parent_kw(model, parent_kw)
|
|
200
211
|
payload = _validate_body(model, alias, target, body)
|
|
212
|
+
if isinstance(h, Mapping):
|
|
213
|
+
if isinstance(payload, Mapping):
|
|
214
|
+
payload = {**payload, **dict(h)}
|
|
215
|
+
else:
|
|
216
|
+
payload = [{**dict(item), **dict(h)} for item in payload]
|
|
201
217
|
is_seq = (
|
|
202
218
|
target in {"create", "update", "replace", "merge"}
|
|
203
219
|
and isinstance(payload, Sequence)
|
|
@@ -248,6 +264,11 @@ def _make_collection_endpoint(
|
|
|
248
264
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
249
265
|
annotation=Annotated[Any, Depends(db_dep)],
|
|
250
266
|
),
|
|
267
|
+
inspect.Parameter(
|
|
268
|
+
"h",
|
|
269
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
270
|
+
annotation=Annotated[Mapping[str, Any], Depends(hdr_dep)],
|
|
271
|
+
),
|
|
251
272
|
inspect.Parameter(
|
|
252
273
|
"body",
|
|
253
274
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
@@ -45,6 +45,8 @@ from .routing import (
|
|
|
45
45
|
_default_path_suffix,
|
|
46
46
|
_normalize_deps,
|
|
47
47
|
_normalize_secdeps,
|
|
48
|
+
_require_auth_header,
|
|
49
|
+
_requires_auth_header,
|
|
48
50
|
_path_for_spec,
|
|
49
51
|
_request_model_for,
|
|
50
52
|
_response_model_for,
|
|
@@ -106,6 +108,8 @@ __all__ = [
|
|
|
106
108
|
"_optionalize_list_in_model",
|
|
107
109
|
"_normalize_deps",
|
|
108
110
|
"_normalize_secdeps",
|
|
111
|
+
"_require_auth_header",
|
|
112
|
+
"_requires_auth_header",
|
|
109
113
|
"_status_for",
|
|
110
114
|
"_RESPONSES_META",
|
|
111
115
|
"_DEFAULT_METHODS",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Mapping, Any
|
|
5
|
+
|
|
6
|
+
from fastapi import Header
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _build_signature_with_header_params(
|
|
10
|
+
hdr_fields: list[tuple[str, str, bool]],
|
|
11
|
+
) -> inspect.Signature:
|
|
12
|
+
params: list[inspect.Parameter] = []
|
|
13
|
+
for _field, header, required in hdr_fields:
|
|
14
|
+
param = header.lower().replace("-", "_")
|
|
15
|
+
default = Header(... if required else None, alias=header)
|
|
16
|
+
params.append(
|
|
17
|
+
inspect.Parameter(
|
|
18
|
+
name=param,
|
|
19
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
20
|
+
default=default,
|
|
21
|
+
annotation=str | None,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
return inspect.Signature(parameters=params, return_annotation=Mapping[str, object])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _make_header_dep(model: type, alias: str):
|
|
28
|
+
hdr_fields: list[tuple[str, str, bool]] = []
|
|
29
|
+
for name, spec in getattr(model, "__tigrbl_cols__", {}).items():
|
|
30
|
+
io = getattr(spec, "io", None)
|
|
31
|
+
if not io or not getattr(io, "header_in", None):
|
|
32
|
+
continue
|
|
33
|
+
if alias not in set(getattr(io, "in_verbs", ()) or ()): # honor IO.in_verbs
|
|
34
|
+
continue
|
|
35
|
+
hdr_fields.append(
|
|
36
|
+
(name, io.header_in, bool(getattr(io, "header_required_in", False)))
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
async def _dep(**kw: Any) -> Mapping[str, object]:
|
|
40
|
+
out: dict[str, object] = {}
|
|
41
|
+
for field, header, _req in hdr_fields:
|
|
42
|
+
param = header.lower().replace("-", "_")
|
|
43
|
+
v = kw.get(param)
|
|
44
|
+
if v is not None:
|
|
45
|
+
out[field] = v
|
|
46
|
+
return out
|
|
47
|
+
|
|
48
|
+
_dep.__signature__ = _build_signature_with_header_params(hdr_fields)
|
|
49
|
+
return _dep
|
|
@@ -35,6 +35,8 @@ from .common import (
|
|
|
35
35
|
_status_for,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
+
from .io_headers import _make_header_dep
|
|
39
|
+
|
|
38
40
|
from ...runtime.executor.types import _Ctx
|
|
39
41
|
|
|
40
42
|
|
|
@@ -58,6 +60,7 @@ def _make_member_endpoint(
|
|
|
58
60
|
real_pk = _pk_name(model)
|
|
59
61
|
pk_names = _pk_names(model)
|
|
60
62
|
nested_vars = list(nested_vars or [])
|
|
63
|
+
hdr_dep = _make_header_dep(model, alias)
|
|
61
64
|
|
|
62
65
|
# --- No body on GET read / DELETE delete ---
|
|
63
66
|
if target in {"read", "delete"}:
|
|
@@ -66,11 +69,14 @@ def _make_member_endpoint(
|
|
|
66
69
|
item_id: Any,
|
|
67
70
|
request: Request,
|
|
68
71
|
db: Any = Depends(db_dep),
|
|
72
|
+
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
69
73
|
**kw: Any,
|
|
70
74
|
):
|
|
71
75
|
parent_kw = {k: kw[k] for k in nested_vars if k in kw}
|
|
72
76
|
_coerce_parent_kw(model, parent_kw)
|
|
73
77
|
payload: Mapping[str, Any] = dict(parent_kw)
|
|
78
|
+
if isinstance(h, Mapping):
|
|
79
|
+
payload = {**payload, **dict(h)}
|
|
74
80
|
path_params = {real_pk: item_id, pk_param: item_id, **parent_kw}
|
|
75
81
|
ctx: Dict[str, Any] = {
|
|
76
82
|
"request": request,
|
|
@@ -142,6 +148,11 @@ def _make_member_endpoint(
|
|
|
142
148
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
143
149
|
annotation=Annotated[Any, Depends(db_dep)],
|
|
144
150
|
),
|
|
151
|
+
inspect.Parameter(
|
|
152
|
+
"h",
|
|
153
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
154
|
+
annotation=Annotated[Mapping[str, Any], Depends(hdr_dep)],
|
|
155
|
+
),
|
|
145
156
|
]
|
|
146
157
|
)
|
|
147
158
|
_endpoint.__signature__ = inspect.Signature(params)
|
|
@@ -257,12 +268,15 @@ def _make_member_endpoint(
|
|
|
257
268
|
item_id: Any,
|
|
258
269
|
request: Request,
|
|
259
270
|
db: Any = Depends(db_dep),
|
|
271
|
+
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
260
272
|
body=body_default,
|
|
261
273
|
**kw: Any,
|
|
262
274
|
):
|
|
263
275
|
parent_kw = {k: kw[k] for k in nested_vars if k in kw}
|
|
264
276
|
_coerce_parent_kw(model, parent_kw)
|
|
265
277
|
payload = _validate_body(model, alias, target, body)
|
|
278
|
+
if isinstance(h, Mapping):
|
|
279
|
+
payload = {**payload, **dict(h)}
|
|
266
280
|
|
|
267
281
|
# Enforce path-PK canonicality. If body echoes PK: drop if equal, 409 if mismatch.
|
|
268
282
|
for k in pk_names:
|
|
@@ -350,6 +364,11 @@ def _make_member_endpoint(
|
|
|
350
364
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
351
365
|
annotation=Annotated[Any, Depends(db_dep)],
|
|
352
366
|
),
|
|
367
|
+
inspect.Parameter(
|
|
368
|
+
"h",
|
|
369
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
370
|
+
annotation=Annotated[Mapping[str, Any], Depends(hdr_dep)],
|
|
371
|
+
),
|
|
353
372
|
inspect.Parameter(
|
|
354
373
|
"body",
|
|
355
374
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
@@ -25,6 +25,8 @@ from .common import (
|
|
|
25
25
|
_normalize_secdeps,
|
|
26
26
|
_optionalize_list_in_model,
|
|
27
27
|
_path_for_spec,
|
|
28
|
+
_require_auth_header,
|
|
29
|
+
_requires_auth_header,
|
|
28
30
|
_req_state_db,
|
|
29
31
|
_resource_name,
|
|
30
32
|
_status,
|
|
@@ -265,6 +267,8 @@ def _build_router(
|
|
|
265
267
|
|
|
266
268
|
secdeps: list[Any] = []
|
|
267
269
|
if auth_dep and sp.alias not in allow_anon and sp.target not in allow_anon:
|
|
270
|
+
if _requires_auth_header(auth_dep):
|
|
271
|
+
secdeps.append(_require_auth_header)
|
|
268
272
|
secdeps.append(auth_dep)
|
|
269
273
|
secdeps.extend(getattr(sp, "secdeps", ()))
|
|
270
274
|
route_secdeps = _normalize_secdeps(secdeps)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
import inspect
|
|
2
3
|
import logging
|
|
3
4
|
|
|
4
5
|
from types import SimpleNamespace
|
|
5
6
|
from typing import Any, Dict, Optional, Sequence, Tuple
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
from .
|
|
9
|
+
from fastapi.security import HTTPBearer
|
|
10
|
+
from .fastapi import Depends, HTTPException, Request, Security, _status
|
|
9
11
|
from ...op import OpSpec
|
|
10
12
|
from ...op.types import CANON
|
|
11
13
|
|
|
@@ -35,6 +37,24 @@ def _normalize_secdeps(secdeps: Optional[Sequence[Any]]) -> list[Any]:
|
|
|
35
37
|
return out
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
def _requires_auth_header(auth_dep: Any) -> bool:
|
|
41
|
+
try:
|
|
42
|
+
sig = inspect.signature(auth_dep)
|
|
43
|
+
except (TypeError, ValueError):
|
|
44
|
+
return False
|
|
45
|
+
for param in sig.parameters.values():
|
|
46
|
+
default = param.default
|
|
47
|
+
dep = getattr(default, "dependency", None)
|
|
48
|
+
if isinstance(dep, HTTPBearer) and getattr(dep, "auto_error", True):
|
|
49
|
+
return True
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _require_auth_header(request: Request) -> None:
|
|
54
|
+
if not request.headers.get("Authorization"):
|
|
55
|
+
raise HTTPException(status_code=_status.HTTP_403_FORBIDDEN, detail="Forbidden")
|
|
56
|
+
|
|
57
|
+
|
|
38
58
|
def _status_for(sp: OpSpec) -> int:
|
|
39
59
|
if sp.status_code is not None:
|
|
40
60
|
return sp.status_code
|
|
@@ -57,6 +57,9 @@ class IOSpec:
|
|
|
57
57
|
mutable_verbs: Tuple[str, ...] = ()
|
|
58
58
|
alias_in: str | None = None
|
|
59
59
|
alias_out: str | None = None
|
|
60
|
+
header_in: str | None = None # e.g., "X-Worker-Key"
|
|
61
|
+
header_out: str | None = None # e.g., "ETag"
|
|
62
|
+
header_required_in: bool = False
|
|
60
63
|
sensitive: bool = False
|
|
61
64
|
redact_last: int | None = None
|
|
62
65
|
filter_ops: Tuple[str, ...] = ()
|
|
@@ -24,3 +24,22 @@ __all__ = [
|
|
|
24
24
|
"Engine",
|
|
25
25
|
"engine",
|
|
26
26
|
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Optional engine plugin support
|
|
30
|
+
from .plugins import load_engine_plugins
|
|
31
|
+
from .registry import register_engine, known_engine_kinds, get_engine_registration
|
|
32
|
+
|
|
33
|
+
# Load external engines automatically on import (idempotent)
|
|
34
|
+
try:
|
|
35
|
+
load_engine_plugins()
|
|
36
|
+
except Exception:
|
|
37
|
+
# Import-time plugin load should never fail the package import
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
__all__ += [
|
|
41
|
+
"load_engine_plugins",
|
|
42
|
+
"register_engine",
|
|
43
|
+
"known_engine_kinds",
|
|
44
|
+
"get_engine_registration",
|
|
45
|
+
]
|