tigrbl 0.3.2.dev1__tar.gz → 0.3.3__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.2.dev1 → tigrbl-0.3.3}/PKG-INFO +1 -1
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/pyproject.toml +1 -1
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/_api.py +5 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/tigrbl_api.py +6 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/_app.py +24 -0
- tigrbl-0.3.3/tigrbl/app/mro_collect.py +106 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/tigrbl_app.py +93 -2
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/model_helpers.py +10 -10
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/collection.py +77 -3
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/member.py +3 -3
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/field_spec.py +5 -4
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/shortcuts.py +14 -3
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/__init__.py +6 -1
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/decorators.py +8 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/build_schema.py +6 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/hookz.py +1 -2
- tigrbl-0.3.3/tigrbl/system/diagnostics/methodz.py +43 -0
- tigrbl-0.3.3/tigrbl/table/_base.py +439 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/table/mro_collect.py +22 -5
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/__init__.py +1 -0
- tigrbl-0.3.2.dev1/tigrbl/app/mro_collect.py +0 -67
- tigrbl-0.3.2.dev1/tigrbl/system/diagnostics/methodz.py +0 -43
- tigrbl-0.3.2.dev1/tigrbl/table/_base.py +0 -271
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/LICENSE +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/api_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/mro_collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/api/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/_model_registry.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/app_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/app/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/api/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/api/common.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/api/include.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/api/resource_proxy.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/api/rpc.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/columns.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/builder.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/ctx.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/identifiers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/namespaces.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/handlers/steps.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/hooks.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/model.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/model_registry.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/attach.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/common.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/fastapi.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/helpers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/io.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/io_headers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/router.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rest/routing.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/rpc.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/schemas/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/schemas/builder.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/schemas/defaults.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/bindings/schemas/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/_column.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/column_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/core.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/jsonhints.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/planning.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/infer/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/io_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/mro_collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/column/storage_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/config/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/config/constants.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/config/defaults.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/config/resolver.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/bulk.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/db.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/enum.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/filters.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/model.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/helpers/normalize.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/core/crud/ops.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/ddl/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/fastapi.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/favicon.svg +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/jinja.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/pydantic.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/sqlalchemy.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/deps/starlette.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/docs/verbosity.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/_engine.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/bind.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/builders.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/capabilities.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/docs/PLUGINS.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/engine_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/plugins.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/registry.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/resolver.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/engine/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/_hook.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/hook_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/mro_collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/hook/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/_op.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/canonical.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/model_registry.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/mro_collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/resolver.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/op/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/_RowBound.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/bootstrappable.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/bound.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/edges.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/fields.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/hierarchy.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/key_digest.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/lifecycle.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/locks.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/markers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/operations.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/ownable.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/principals.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/tenant_bound.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/upsertable.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/mixins/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/_base.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/audit.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/client.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/group.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/org.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/rbac.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/status.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/tenant.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/orm/tables/user.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/bind.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/resolver.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/response/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/rest/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/emit/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/emit/paired_post.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/emit/paired_pre.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/emit/readtime_alias.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/out/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/out/masking.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/refresh/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/refresh/demand.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/resolve/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/resolve/assemble.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/resolve/paired_gen.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/headers_from_payload.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/negotiate.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/negotiation.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/render.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/renderer.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/template.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/response/templates.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/schema/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/schema/collect_in.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/schema/collect_out.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/storage/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/storage/to_stored.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/wire/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/wire/build_in.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/wire/build_out.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/wire/dump.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/atoms/wire/validate_in.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/context.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/errors/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/errors/converters.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/errors/exceptions.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/errors/mappings.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/errors/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/events.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/executor/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/executor/guards.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/executor/helpers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/executor/invoke.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/executor/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/kernel.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/labels.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/opview.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/ordering.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/system.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/runtime/trace.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/_schema.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/cache.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/compat.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/extras.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/helpers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/list_params.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/builder/strip_parent_fields.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/collect.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/get_schema.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/schema_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/types.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/schema/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/README.md +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/abc.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/base.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/decorators.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/default.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/session/spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/specs.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/compat.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/healthz.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/kernelz.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/router.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/diagnostics/utils.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/system/uvicorn.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/table/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/table/_table.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/table/shortcuts.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/table/table_spec.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/jsonrpc/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/jsonrpc/dispatcher.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/jsonrpc/helpers.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/jsonrpc/models.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/rest/__init__.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/transport/rest/aggregator.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/allow_anon_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/authn_abc.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/nested_path_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/op.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/op_config_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/op_verb_alias_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/request_extras_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/response_extras_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/table_config_provider.py +0 -0
- {tigrbl-0.3.2.dev1 → tigrbl-0.3.3}/tigrbl/types/uuid.py +0 -0
|
@@ -26,6 +26,11 @@ class Api(APISpec, ApiRouter):
|
|
|
26
26
|
def __eq__(self, other: object) -> bool: # pragma: no cover - identity compare
|
|
27
27
|
return self is other
|
|
28
28
|
|
|
29
|
+
@property
|
|
30
|
+
def router(self) -> "Api": # pragma: no cover - simple alias
|
|
31
|
+
"""Mirror FastAPI-style router access for API instances."""
|
|
32
|
+
return self
|
|
33
|
+
|
|
29
34
|
def __init__(
|
|
30
35
|
self, *, engine: EngineCfg | None = None, **router_kwargs: Any
|
|
31
36
|
) -> None:
|
|
@@ -62,6 +62,8 @@ class TigrblApi(_Api):
|
|
|
62
62
|
self,
|
|
63
63
|
*,
|
|
64
64
|
engine: EngineCfg | None = None,
|
|
65
|
+
models: Sequence[type] | None = None,
|
|
66
|
+
prefix: str | None = None,
|
|
65
67
|
jsonrpc_prefix: str = "/rpc",
|
|
66
68
|
system_prefix: str = "/system",
|
|
67
69
|
api_hooks: Mapping[str, Iterable[Callable]]
|
|
@@ -69,6 +71,8 @@ class TigrblApi(_Api):
|
|
|
69
71
|
| None = None,
|
|
70
72
|
**router_kwargs: Any,
|
|
71
73
|
) -> None:
|
|
74
|
+
if prefix is not None:
|
|
75
|
+
self.PREFIX = prefix
|
|
72
76
|
_Api.__init__(self, engine=engine, **router_kwargs)
|
|
73
77
|
self.jsonrpc_prefix = jsonrpc_prefix
|
|
74
78
|
self.system_prefix = system_prefix
|
|
@@ -89,6 +93,8 @@ class TigrblApi(_Api):
|
|
|
89
93
|
|
|
90
94
|
# API-level hooks map (merged into each model at include-time; precedence handled in bindings.hooks)
|
|
91
95
|
self._api_hooks_map = copy.deepcopy(api_hooks) if api_hooks else None
|
|
96
|
+
if models:
|
|
97
|
+
self.include_models(list(models))
|
|
92
98
|
|
|
93
99
|
# ------------------------- internal helpers -------------------------
|
|
94
100
|
|
|
@@ -12,12 +12,36 @@ from .app_spec import AppSpec
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class App(AppSpec, FastAPI):
|
|
15
|
+
TITLE = "Tigrbl"
|
|
16
|
+
VERSION = "0.1.0"
|
|
17
|
+
LIFESPAN = None
|
|
18
|
+
MIDDLEWARES = ()
|
|
19
|
+
APIS = ()
|
|
20
|
+
OPS = ()
|
|
21
|
+
MODELS = ()
|
|
22
|
+
SCHEMAS = ()
|
|
23
|
+
HOOKS = ()
|
|
24
|
+
SECURITY_DEPS = ()
|
|
25
|
+
DEPS = ()
|
|
26
|
+
RESPONSE = None
|
|
27
|
+
JSONRPC_PREFIX = "/rpc"
|
|
28
|
+
SYSTEM_PREFIX = "/system"
|
|
29
|
+
|
|
15
30
|
def __init__(
|
|
16
31
|
self, *, engine: EngineCfg | None = None, **fastapi_kwargs: Any
|
|
17
32
|
) -> None:
|
|
18
33
|
# Manually mirror ``AppSpec`` fields so the dataclass-generated ``repr``
|
|
19
34
|
# and friends have expected attributes while runtime structures remain
|
|
20
35
|
# mutable dictionaries or lists as needed.
|
|
36
|
+
title = fastapi_kwargs.pop("title", None)
|
|
37
|
+
if title is not None:
|
|
38
|
+
self.TITLE = title
|
|
39
|
+
version = fastapi_kwargs.pop("version", None)
|
|
40
|
+
if version is not None:
|
|
41
|
+
self.VERSION = version
|
|
42
|
+
lifespan = fastapi_kwargs.pop("lifespan", None)
|
|
43
|
+
if lifespan is not None:
|
|
44
|
+
self.LIFESPAN = lifespan
|
|
21
45
|
self.title = self.TITLE
|
|
22
46
|
self.version = self.VERSION
|
|
23
47
|
self.engine = engine if engine is not None else getattr(self, "ENGINE", None)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
from typing import Any, Tuple
|
|
6
|
+
|
|
7
|
+
from .app_spec import AppSpec
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("uvicorn")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _merge_seq_attr(
|
|
13
|
+
app: type,
|
|
14
|
+
attr: str,
|
|
15
|
+
*,
|
|
16
|
+
include_inherited: bool = False,
|
|
17
|
+
reverse: bool = False,
|
|
18
|
+
) -> Tuple[Any, ...]:
|
|
19
|
+
values: list[Any] = []
|
|
20
|
+
mro = reversed(app.__mro__) if reverse else app.__mro__
|
|
21
|
+
for base in mro:
|
|
22
|
+
if include_inherited:
|
|
23
|
+
if not hasattr(base, attr):
|
|
24
|
+
continue
|
|
25
|
+
seq = getattr(base, attr) or ()
|
|
26
|
+
else:
|
|
27
|
+
seq = base.__dict__.get(attr, ()) or ()
|
|
28
|
+
try:
|
|
29
|
+
values.extend(seq)
|
|
30
|
+
except TypeError: # non-iterable
|
|
31
|
+
values.append(seq)
|
|
32
|
+
return tuple(values)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@lru_cache(maxsize=None)
|
|
36
|
+
def mro_collect_app_spec(app: type) -> AppSpec:
|
|
37
|
+
"""Collect AppSpec-like declarations across the app's MRO."""
|
|
38
|
+
logger.info("Collecting app spec for %s", app.__name__)
|
|
39
|
+
|
|
40
|
+
sentinel = object()
|
|
41
|
+
title: Any = sentinel
|
|
42
|
+
version: Any = sentinel
|
|
43
|
+
engine: Any | None = sentinel # type: ignore[assignment]
|
|
44
|
+
response: Any = sentinel
|
|
45
|
+
jsonrpc_prefix: Any = sentinel
|
|
46
|
+
system_prefix: Any = sentinel
|
|
47
|
+
lifespan: Any = sentinel
|
|
48
|
+
|
|
49
|
+
for base in app.__mro__:
|
|
50
|
+
if "TITLE" in base.__dict__ and title is sentinel:
|
|
51
|
+
title = base.__dict__["TITLE"]
|
|
52
|
+
if "VERSION" in base.__dict__ and version is sentinel:
|
|
53
|
+
version = base.__dict__["VERSION"]
|
|
54
|
+
if "ENGINE" in base.__dict__ and engine is sentinel:
|
|
55
|
+
engine = base.__dict__["ENGINE"]
|
|
56
|
+
if "RESPONSE" in base.__dict__ and response is sentinel:
|
|
57
|
+
response = base.__dict__["RESPONSE"]
|
|
58
|
+
if "JSONRPC_PREFIX" in base.__dict__ and jsonrpc_prefix is sentinel:
|
|
59
|
+
jsonrpc_prefix = base.__dict__["JSONRPC_PREFIX"]
|
|
60
|
+
if "SYSTEM_PREFIX" in base.__dict__ and system_prefix is sentinel:
|
|
61
|
+
system_prefix = base.__dict__["SYSTEM_PREFIX"]
|
|
62
|
+
if "LIFESPAN" in base.__dict__ and lifespan is sentinel:
|
|
63
|
+
lifespan = base.__dict__["LIFESPAN"]
|
|
64
|
+
|
|
65
|
+
if title is sentinel:
|
|
66
|
+
title = "Tigrbl"
|
|
67
|
+
if version is sentinel:
|
|
68
|
+
version = "0.1.0"
|
|
69
|
+
if engine is sentinel:
|
|
70
|
+
engine = None
|
|
71
|
+
if response is sentinel:
|
|
72
|
+
response = None
|
|
73
|
+
if jsonrpc_prefix is sentinel:
|
|
74
|
+
jsonrpc_prefix = "/rpc"
|
|
75
|
+
if system_prefix is sentinel:
|
|
76
|
+
system_prefix = "/system"
|
|
77
|
+
if lifespan is sentinel:
|
|
78
|
+
lifespan = None
|
|
79
|
+
|
|
80
|
+
include_inherited_apis = "APIS" not in app.__dict__
|
|
81
|
+
spec = AppSpec(
|
|
82
|
+
title=title,
|
|
83
|
+
version=version,
|
|
84
|
+
engine=engine,
|
|
85
|
+
apis=_merge_seq_attr(
|
|
86
|
+
app,
|
|
87
|
+
"APIS",
|
|
88
|
+
include_inherited=include_inherited_apis,
|
|
89
|
+
reverse=include_inherited_apis,
|
|
90
|
+
),
|
|
91
|
+
ops=_merge_seq_attr(app, "OPS"),
|
|
92
|
+
models=_merge_seq_attr(app, "MODELS"),
|
|
93
|
+
schemas=_merge_seq_attr(app, "SCHEMAS"),
|
|
94
|
+
hooks=_merge_seq_attr(app, "HOOKS"),
|
|
95
|
+
security_deps=_merge_seq_attr(app, "SECURITY_DEPS"),
|
|
96
|
+
deps=_merge_seq_attr(app, "DEPS"),
|
|
97
|
+
response=response,
|
|
98
|
+
jsonrpc_prefix=jsonrpc_prefix,
|
|
99
|
+
system_prefix=system_prefix,
|
|
100
|
+
middlewares=_merge_seq_attr(app, "MIDDLEWARES"),
|
|
101
|
+
lifespan=lifespan,
|
|
102
|
+
)
|
|
103
|
+
return spec
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
__all__ = ["mro_collect_app_spec"]
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# tigrbl/v3/app/tigrbl_app.py
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import asyncio
|
|
4
5
|
import copy
|
|
6
|
+
import inspect
|
|
5
7
|
from types import SimpleNamespace
|
|
6
8
|
from typing import (
|
|
7
9
|
Any,
|
|
@@ -71,6 +73,7 @@ class TigrblApp(_App):
|
|
|
71
73
|
self,
|
|
72
74
|
*,
|
|
73
75
|
engine: EngineCfg | None = None,
|
|
76
|
+
apis: Sequence[Any] | None = None,
|
|
74
77
|
jsonrpc_prefix: str = "/rpc",
|
|
75
78
|
system_prefix: str = "/system",
|
|
76
79
|
api_hooks: Mapping[str, Iterable[Callable]]
|
|
@@ -106,9 +109,13 @@ class TigrblApp(_App):
|
|
|
106
109
|
self.table_config: Dict[str, Dict[str, Any]] = {}
|
|
107
110
|
self.core = SimpleNamespace()
|
|
108
111
|
self.core_raw = SimpleNamespace()
|
|
112
|
+
self.apis = list(getattr(self, "APIS", ()))
|
|
109
113
|
|
|
110
114
|
# API-level hooks map (merged into each model at include-time; precedence handled in bindings.hooks)
|
|
111
115
|
self._api_hooks_map = copy.deepcopy(api_hooks) if api_hooks else None
|
|
116
|
+
if apis:
|
|
117
|
+
self.apis.extend(list(apis))
|
|
118
|
+
self.include_apis(self.apis)
|
|
112
119
|
|
|
113
120
|
# ------------------------- internal helpers -------------------------
|
|
114
121
|
|
|
@@ -175,6 +182,92 @@ class TigrblApp(_App):
|
|
|
175
182
|
mount_router=mount_router,
|
|
176
183
|
)
|
|
177
184
|
|
|
185
|
+
def include_api(
|
|
186
|
+
self,
|
|
187
|
+
api: Any,
|
|
188
|
+
*,
|
|
189
|
+
prefix: str | None = None,
|
|
190
|
+
mount_router: bool = True,
|
|
191
|
+
) -> Any:
|
|
192
|
+
"""Mount a Tigrbl API router onto this app and track it."""
|
|
193
|
+
if api not in self.apis:
|
|
194
|
+
self.apis.append(api)
|
|
195
|
+
if not mount_router:
|
|
196
|
+
return api
|
|
197
|
+
router = getattr(api, "router", api)
|
|
198
|
+
if hasattr(self, "include_router"):
|
|
199
|
+
self.include_router(router, prefix=prefix or "")
|
|
200
|
+
return api
|
|
201
|
+
|
|
202
|
+
def include_router(self, router: Any, *args: Any, **kwargs: Any) -> None:
|
|
203
|
+
"""Extend FastAPI include_router to track Tigrbl APIs."""
|
|
204
|
+
if hasattr(router, "models") and hasattr(router, "initialize"):
|
|
205
|
+
self.include_api(
|
|
206
|
+
router,
|
|
207
|
+
prefix=kwargs.get("prefix"),
|
|
208
|
+
mount_router=False,
|
|
209
|
+
)
|
|
210
|
+
super().include_router(router, *args, **kwargs)
|
|
211
|
+
|
|
212
|
+
def include_apis(self, apis: Sequence[Any]) -> None:
|
|
213
|
+
"""Mount multiple APIs, supporting optional per-item prefixes."""
|
|
214
|
+
for entry in apis:
|
|
215
|
+
prefix = None
|
|
216
|
+
api = entry
|
|
217
|
+
if isinstance(entry, tuple) and entry:
|
|
218
|
+
api = entry[0]
|
|
219
|
+
if len(entry) > 1:
|
|
220
|
+
value = entry[1]
|
|
221
|
+
if isinstance(value, dict):
|
|
222
|
+
prefix = value.get("prefix")
|
|
223
|
+
elif isinstance(value, str):
|
|
224
|
+
prefix = value
|
|
225
|
+
self.include_api(api, prefix=prefix)
|
|
226
|
+
|
|
227
|
+
def initialize(
|
|
228
|
+
self,
|
|
229
|
+
*,
|
|
230
|
+
schemas: Iterable[str] | None = None,
|
|
231
|
+
sqlite_attachments: Mapping[str, str] | None = None,
|
|
232
|
+
tables: Iterable[Any] | None = None,
|
|
233
|
+
):
|
|
234
|
+
"""Initialize DDL for the app and any attached APIs."""
|
|
235
|
+
result = _ddl_initialize(
|
|
236
|
+
self,
|
|
237
|
+
schemas=schemas,
|
|
238
|
+
sqlite_attachments=sqlite_attachments,
|
|
239
|
+
tables=tables,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
api_results = []
|
|
243
|
+
for api in self.apis:
|
|
244
|
+
init = getattr(api, "initialize", None)
|
|
245
|
+
if callable(init):
|
|
246
|
+
api_results.append(
|
|
247
|
+
init(
|
|
248
|
+
schemas=schemas,
|
|
249
|
+
sqlite_attachments=sqlite_attachments,
|
|
250
|
+
tables=tables,
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
awaitables = [r for r in [result, *api_results] if inspect.isawaitable(r)]
|
|
255
|
+
if not awaitables:
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
async def _inner():
|
|
259
|
+
for item in [result, *api_results]:
|
|
260
|
+
if inspect.isawaitable(item):
|
|
261
|
+
await item
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
loop = asyncio.get_running_loop()
|
|
265
|
+
except RuntimeError:
|
|
266
|
+
asyncio.run(_inner())
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
return loop.create_task(_inner())
|
|
270
|
+
|
|
178
271
|
async def rpc_call(
|
|
179
272
|
self,
|
|
180
273
|
model_or_name: type | str,
|
|
@@ -311,8 +404,6 @@ class TigrblApp(_App):
|
|
|
311
404
|
tables.append(t)
|
|
312
405
|
return tables
|
|
313
406
|
|
|
314
|
-
initialize = _ddl_initialize
|
|
315
|
-
|
|
316
407
|
# ------------------------- repr -------------------------
|
|
317
408
|
|
|
318
409
|
def __repr__(self) -> str: # pragma: no cover
|
|
@@ -24,40 +24,40 @@ def _ensure_model_namespaces(model: type) -> None:
|
|
|
24
24
|
"""Create top-level namespaces on the model class if missing."""
|
|
25
25
|
|
|
26
26
|
# op indexes & metadata
|
|
27
|
-
if not
|
|
28
|
-
if
|
|
27
|
+
if "ops" not in model.__dict__:
|
|
28
|
+
if "opspecs" in model.__dict__:
|
|
29
29
|
model.ops = model.opspecs
|
|
30
30
|
else:
|
|
31
31
|
model.ops = SimpleNamespace(all=(), by_key={}, by_alias={})
|
|
32
32
|
# Backwards compatibility: older code may still expect `model.opspecs`
|
|
33
33
|
model.opspecs = model.ops
|
|
34
34
|
# pydantic schemas: .<alias>.in_ / .<alias>.out
|
|
35
|
-
if not
|
|
35
|
+
if "schemas" not in model.__dict__:
|
|
36
36
|
model.schemas = SimpleNamespace()
|
|
37
37
|
# hooks: phase chains & raw hook descriptors if you want to expose them
|
|
38
|
-
if not
|
|
38
|
+
if "hooks" not in model.__dict__:
|
|
39
39
|
model.hooks = SimpleNamespace()
|
|
40
40
|
# handlers: .<alias>.raw (core/custom), .<alias>.handler (HANDLER chain entry point)
|
|
41
|
-
if not
|
|
41
|
+
if "handlers" not in model.__dict__:
|
|
42
42
|
model.handlers = SimpleNamespace()
|
|
43
43
|
# rpc: callables to be registered/mounted elsewhere as JSON-RPC methods
|
|
44
|
-
if not
|
|
44
|
+
if "rpc" not in model.__dict__:
|
|
45
45
|
model.rpc = SimpleNamespace()
|
|
46
46
|
# rest: .router (FastAPI Router or compatible) – built in rest binding
|
|
47
|
-
if not
|
|
47
|
+
if "rest" not in model.__dict__:
|
|
48
48
|
model.rest = SimpleNamespace(router=None)
|
|
49
49
|
# basic table metadata for convenience (introspective only; NEVER used for HTTP paths)
|
|
50
|
-
if not
|
|
50
|
+
if "columns" not in model.__dict__:
|
|
51
51
|
table = getattr(model, "__table__", None)
|
|
52
52
|
cols = tuple(getattr(table, "columns", ()) or ())
|
|
53
53
|
model.columns = tuple(
|
|
54
54
|
getattr(c, "name", None) for c in cols if getattr(c, "name", None)
|
|
55
55
|
)
|
|
56
|
-
if not
|
|
56
|
+
if "table_config" not in model.__dict__:
|
|
57
57
|
table = getattr(model, "__table__", None)
|
|
58
58
|
model.table_config = dict(getattr(table, "kwargs", {}) or {})
|
|
59
59
|
# ensure raw hook store exists for decorator merges
|
|
60
|
-
if not
|
|
60
|
+
if "__tigrbl_hooks__" not in model.__dict__:
|
|
61
61
|
setattr(model, "__tigrbl_hooks__", {})
|
|
62
62
|
|
|
63
63
|
|
|
@@ -178,7 +178,79 @@ def _make_collection_endpoint(
|
|
|
178
178
|
_endpoint.__signature__ = inspect.Signature(params)
|
|
179
179
|
else:
|
|
180
180
|
body_model = _request_model_for(sp, model)
|
|
181
|
+
if body_model is None and sp.request_model is None and target == "custom":
|
|
182
|
+
|
|
183
|
+
async def _endpoint(
|
|
184
|
+
request: Request,
|
|
185
|
+
db: Any = Depends(db_dep),
|
|
186
|
+
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
187
|
+
**kw: Any,
|
|
188
|
+
):
|
|
189
|
+
parent_kw = {k: kw[k] for k in nested_vars if k in kw}
|
|
190
|
+
_coerce_parent_kw(model, parent_kw)
|
|
191
|
+
payload: Mapping[str, Any] = dict(parent_kw)
|
|
192
|
+
if isinstance(h, Mapping):
|
|
193
|
+
payload = {**payload, **dict(h)}
|
|
194
|
+
ctx = _ctx(model, alias, target, request, db, payload, parent_kw, api)
|
|
195
|
+
|
|
196
|
+
def _serializer(r, _ctx=ctx):
|
|
197
|
+
out = _serialize_output(model, alias, target, sp, r)
|
|
198
|
+
temp = (
|
|
199
|
+
getattr(_ctx, "temp", {}) if isinstance(_ctx, Mapping) else {}
|
|
200
|
+
)
|
|
201
|
+
extras = (
|
|
202
|
+
temp.get("response_extras", {})
|
|
203
|
+
if isinstance(temp, Mapping)
|
|
204
|
+
else {}
|
|
205
|
+
)
|
|
206
|
+
if isinstance(out, dict) and isinstance(extras, dict):
|
|
207
|
+
out.update(extras)
|
|
208
|
+
return out
|
|
209
|
+
|
|
210
|
+
ctx["response_serializer"] = _serializer
|
|
211
|
+
phases = _get_phase_chains(model, alias)
|
|
212
|
+
result = await _executor._invoke(
|
|
213
|
+
request=request,
|
|
214
|
+
db=db,
|
|
215
|
+
phases=phases,
|
|
216
|
+
ctx=ctx,
|
|
217
|
+
)
|
|
218
|
+
return result
|
|
219
|
+
|
|
220
|
+
_endpoint.__signature__ = _sig(
|
|
221
|
+
nested_vars,
|
|
222
|
+
[
|
|
223
|
+
inspect.Parameter(
|
|
224
|
+
"request",
|
|
225
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
226
|
+
annotation=Request,
|
|
227
|
+
),
|
|
228
|
+
inspect.Parameter(
|
|
229
|
+
"db",
|
|
230
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
231
|
+
annotation=Annotated[Any, Depends(db_dep)],
|
|
232
|
+
),
|
|
233
|
+
inspect.Parameter(
|
|
234
|
+
"h",
|
|
235
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
236
|
+
annotation=Annotated[Mapping[str, Any], Depends(hdr_dep)],
|
|
237
|
+
),
|
|
238
|
+
],
|
|
239
|
+
)
|
|
240
|
+
_endpoint.__name__ = f"rest_{model.__name__}_{alias}_collection"
|
|
241
|
+
_endpoint.__qualname__ = _endpoint.__name__
|
|
242
|
+
_endpoint.__doc__ = (
|
|
243
|
+
f"REST collection endpoint for {model.__name__}.{alias} ({target})"
|
|
244
|
+
)
|
|
245
|
+
return _endpoint
|
|
246
|
+
|
|
181
247
|
base = body_model or Mapping[str, Any]
|
|
248
|
+
body_required = target in {
|
|
249
|
+
"create",
|
|
250
|
+
"update",
|
|
251
|
+
"replace",
|
|
252
|
+
"merge",
|
|
253
|
+
} or target.startswith("bulk_")
|
|
182
254
|
if target.startswith("bulk_"):
|
|
183
255
|
alias_ns = getattr(
|
|
184
256
|
getattr(model, "schemas", None) or SimpleNamespace(), alias, None
|
|
@@ -197,13 +269,13 @@ def _make_collection_endpoint(
|
|
|
197
269
|
_list_ann(Mapping[str, Any]),
|
|
198
270
|
)
|
|
199
271
|
else:
|
|
200
|
-
body_annotation = base
|
|
272
|
+
body_annotation = _union(base, type(None)) if not body_required else base
|
|
201
273
|
|
|
202
274
|
async def _endpoint(
|
|
203
275
|
request: Request,
|
|
204
276
|
db: Any = Depends(db_dep),
|
|
205
277
|
h: Mapping[str, Any] = Depends(hdr_dep),
|
|
206
|
-
body=
|
|
278
|
+
body=None,
|
|
207
279
|
**kw: Any,
|
|
208
280
|
):
|
|
209
281
|
parent_kw = {k: kw[k] for k in nested_vars if k in kw}
|
|
@@ -251,6 +323,7 @@ def _make_collection_endpoint(
|
|
|
251
323
|
return result
|
|
252
324
|
return result
|
|
253
325
|
|
|
326
|
+
body_default = ... if body_required else None
|
|
254
327
|
_endpoint.__signature__ = _sig(
|
|
255
328
|
nested_vars,
|
|
256
329
|
[
|
|
@@ -272,7 +345,8 @@ def _make_collection_endpoint(
|
|
|
272
345
|
inspect.Parameter(
|
|
273
346
|
"body",
|
|
274
347
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
275
|
-
annotation=Annotated[body_annotation, Body(
|
|
348
|
+
annotation=Annotated[body_annotation, Body()],
|
|
349
|
+
default=body_default,
|
|
276
350
|
),
|
|
277
351
|
],
|
|
278
352
|
)
|
|
@@ -134,7 +134,7 @@ def _make_member_endpoint(
|
|
|
134
134
|
params.extend(
|
|
135
135
|
[
|
|
136
136
|
inspect.Parameter(
|
|
137
|
-
|
|
137
|
+
pk_param,
|
|
138
138
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
139
139
|
annotation=Annotated[Any, Path(...)],
|
|
140
140
|
),
|
|
@@ -230,7 +230,7 @@ def _make_member_endpoint(
|
|
|
230
230
|
params.extend(
|
|
231
231
|
[
|
|
232
232
|
inspect.Parameter(
|
|
233
|
-
|
|
233
|
+
pk_param,
|
|
234
234
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
235
235
|
annotation=Annotated[Any, Path(...)],
|
|
236
236
|
),
|
|
@@ -350,7 +350,7 @@ def _make_member_endpoint(
|
|
|
350
350
|
params.extend(
|
|
351
351
|
[
|
|
352
352
|
inspect.Parameter(
|
|
353
|
-
|
|
353
|
+
pk_param,
|
|
354
354
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
355
355
|
annotation=Annotated[Any, Path(...)],
|
|
356
356
|
),
|
|
@@ -15,16 +15,17 @@ class FieldSpec:
|
|
|
15
15
|
``py_type`` denotes the expected Python type and may be omitted when the
|
|
16
16
|
model attribute is annotated; the type will then be inferred. ``constraints``
|
|
17
17
|
mirrors arguments accepted by :func:`pydantic.Field` and participates in
|
|
18
|
-
schema generation. ``
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
declared type.
|
|
18
|
+
schema generation. ``description`` provides a convenience field for schema
|
|
19
|
+
metadata. ``required_in`` and ``allow_null_in`` govern which API verbs must
|
|
20
|
+
supply the value or may explicitly send ``null`` in requests. Responses rely
|
|
21
|
+
on Pydantic's built-in encoders based solely on the declared type.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
py_type: Any = Any
|
|
25
25
|
|
|
26
26
|
# For request/response schema generation (+ pydantic.Field)
|
|
27
27
|
constraints: Dict[str, Any] = dc_field(default_factory=dict)
|
|
28
|
+
description: str | None = None
|
|
28
29
|
|
|
29
30
|
# Request policy (DB nullability lives in StorageSpec.nullable)
|
|
30
31
|
required_in: Tuple[str, ...] = ()
|
|
@@ -64,10 +64,21 @@ def makeVirtualColumn(
|
|
|
64
64
|
) -> Column:
|
|
65
65
|
"""Convenience for wire-only virtual columns."""
|
|
66
66
|
if spec is not None:
|
|
67
|
-
if any(
|
|
68
|
-
x is not None for x in (field, io, default_factory, producer, read_producer)
|
|
69
|
-
):
|
|
67
|
+
if any(x is not None for x in (field, io, default_factory)):
|
|
70
68
|
raise ValueError("Provide either spec or individual components, not both.")
|
|
69
|
+
if producer is not None and read_producer is not None:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"Provide only one of producer= or read_producer=, not both."
|
|
72
|
+
)
|
|
73
|
+
rp = read_producer or producer
|
|
74
|
+
if rp is not None:
|
|
75
|
+
spec = ColumnSpec(
|
|
76
|
+
storage=spec.storage,
|
|
77
|
+
field=spec.field,
|
|
78
|
+
io=spec.io,
|
|
79
|
+
default_factory=spec.default_factory,
|
|
80
|
+
read_producer=rp,
|
|
81
|
+
)
|
|
71
82
|
return Column(spec=spec, **kw)
|
|
72
83
|
if producer is not None and read_producer is not None:
|
|
73
84
|
raise ValueError("Provide only one of producer= or read_producer=, not both.")
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
from .decorators import
|
|
1
|
+
from .decorators import (
|
|
2
|
+
response_ctx,
|
|
3
|
+
get_attached_response_spec,
|
|
4
|
+
get_attached_response_alias,
|
|
5
|
+
)
|
|
2
6
|
from .types import (
|
|
3
7
|
Response,
|
|
4
8
|
ResponseKind,
|
|
@@ -14,6 +18,7 @@ from ..runtime.atoms.response.templates import render_template
|
|
|
14
18
|
__all__ = [
|
|
15
19
|
"response_ctx",
|
|
16
20
|
"get_attached_response_spec",
|
|
21
|
+
"get_attached_response_alias",
|
|
17
22
|
"ResponseSpec",
|
|
18
23
|
"ResponseKind",
|
|
19
24
|
"TemplateSpec",
|
|
@@ -5,6 +5,7 @@ from .types import ResponseSpec
|
|
|
5
5
|
|
|
6
6
|
T = TypeVar("T")
|
|
7
7
|
_ATTR = "__tigrbl_response_spec__"
|
|
8
|
+
_ALIAS_ATTR = "__tigrbl_response_alias__"
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def _to_spec(spec: Optional[ResponseSpec] = None, **kwargs: Any) -> ResponseSpec:
|
|
@@ -24,10 +25,13 @@ def response_ctx(**kwargs: Any) -> Callable[[T], T]: ...
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def response_ctx(*args: Any, **kwargs: Any) -> Callable[[T], T]:
|
|
28
|
+
alias = kwargs.pop("alias", None)
|
|
27
29
|
spec = _to_spec(*args, **kwargs)
|
|
28
30
|
|
|
29
31
|
def decorator(target: T) -> T:
|
|
30
32
|
setattr(target, _ATTR, spec)
|
|
33
|
+
if alias is not None:
|
|
34
|
+
setattr(target, _ALIAS_ATTR, alias)
|
|
31
35
|
return target
|
|
32
36
|
|
|
33
37
|
return decorator
|
|
@@ -35,3 +39,7 @@ def response_ctx(*args: Any, **kwargs: Any) -> Callable[[T], T]:
|
|
|
35
39
|
|
|
36
40
|
def get_attached_response_spec(obj: Any) -> Optional[ResponseSpec]:
|
|
37
41
|
return getattr(obj, _ATTR, None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_attached_response_alias(obj: Any) -> Optional[str]:
|
|
45
|
+
return getattr(obj, _ALIAS_ATTR, None)
|
|
@@ -98,6 +98,9 @@ def _build_schema(
|
|
|
98
98
|
# Field construction (collect kwargs then create Field once)
|
|
99
99
|
fs = getattr(spec, "field", None)
|
|
100
100
|
field_kwargs: Dict[str, Any] = dict(getattr(fs, "constraints", {}) or {})
|
|
101
|
+
description = getattr(fs, "description", None)
|
|
102
|
+
if description and "description" not in field_kwargs:
|
|
103
|
+
field_kwargs["description"] = description
|
|
101
104
|
|
|
102
105
|
default_factory = getattr(spec, "default_factory", None)
|
|
103
106
|
if default_factory and verb in set(getattr(io, "in_verbs", []) or []):
|
|
@@ -163,6 +166,9 @@ def _build_schema(
|
|
|
163
166
|
allow_null = bool(fs and verb in getattr(fs, "allow_null_in", ()))
|
|
164
167
|
nullable = bool(getattr(spec, "nullable", True))
|
|
165
168
|
field_kwargs: Dict[str, Any] = dict(getattr(fs, "constraints", {}) or {})
|
|
169
|
+
description = getattr(fs, "description", None)
|
|
170
|
+
if description and "description" not in field_kwargs:
|
|
171
|
+
field_kwargs["description"] = description
|
|
166
172
|
|
|
167
173
|
default_factory = getattr(spec, "default_factory", None)
|
|
168
174
|
if default_factory and verb in set(getattr(spec.io, "in_verbs", []) or []):
|