tigrbl 0.0.1.dev1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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/README.md +94 -0
- tigrbl/__init__.py +139 -14
- tigrbl/api/__init__.py +6 -0
- tigrbl/api/_api.py +97 -0
- tigrbl/api/api_spec.py +30 -0
- tigrbl/api/mro_collect.py +43 -0
- tigrbl/api/shortcuts.py +56 -0
- tigrbl/api/tigrbl_api.py +291 -0
- tigrbl/app/__init__.py +0 -0
- tigrbl/app/_app.py +86 -0
- tigrbl/app/_model_registry.py +41 -0
- tigrbl/app/app_spec.py +42 -0
- tigrbl/app/mro_collect.py +67 -0
- tigrbl/app/shortcuts.py +65 -0
- tigrbl/app/tigrbl_app.py +319 -0
- tigrbl/bindings/__init__.py +73 -0
- tigrbl/bindings/api/__init__.py +12 -0
- tigrbl/bindings/api/common.py +109 -0
- tigrbl/bindings/api/include.py +256 -0
- tigrbl/bindings/api/resource_proxy.py +149 -0
- tigrbl/bindings/api/rpc.py +111 -0
- tigrbl/bindings/columns.py +49 -0
- tigrbl/bindings/handlers/__init__.py +11 -0
- tigrbl/bindings/handlers/builder.py +119 -0
- tigrbl/bindings/handlers/ctx.py +74 -0
- tigrbl/bindings/handlers/identifiers.py +228 -0
- tigrbl/bindings/handlers/namespaces.py +51 -0
- tigrbl/bindings/handlers/steps.py +276 -0
- tigrbl/bindings/hooks.py +311 -0
- tigrbl/bindings/model.py +194 -0
- tigrbl/bindings/model_helpers.py +139 -0
- tigrbl/bindings/model_registry.py +77 -0
- tigrbl/bindings/rest/__init__.py +7 -0
- tigrbl/bindings/rest/attach.py +34 -0
- tigrbl/bindings/rest/collection.py +286 -0
- tigrbl/bindings/rest/common.py +120 -0
- tigrbl/bindings/rest/fastapi.py +76 -0
- tigrbl/bindings/rest/helpers.py +119 -0
- tigrbl/bindings/rest/io.py +317 -0
- tigrbl/bindings/rest/io_headers.py +49 -0
- tigrbl/bindings/rest/member.py +386 -0
- tigrbl/bindings/rest/router.py +296 -0
- tigrbl/bindings/rest/routing.py +153 -0
- tigrbl/bindings/rpc.py +364 -0
- tigrbl/bindings/schemas/__init__.py +11 -0
- tigrbl/bindings/schemas/builder.py +348 -0
- tigrbl/bindings/schemas/defaults.py +260 -0
- tigrbl/bindings/schemas/utils.py +193 -0
- tigrbl/column/README.md +62 -0
- tigrbl/column/__init__.py +72 -0
- tigrbl/column/_column.py +96 -0
- tigrbl/column/column_spec.py +40 -0
- tigrbl/column/field_spec.py +31 -0
- tigrbl/column/infer/__init__.py +25 -0
- tigrbl/column/infer/core.py +92 -0
- tigrbl/column/infer/jsonhints.py +44 -0
- tigrbl/column/infer/planning.py +133 -0
- tigrbl/column/infer/types.py +102 -0
- tigrbl/column/infer/utils.py +59 -0
- tigrbl/column/io_spec.py +136 -0
- tigrbl/column/mro_collect.py +59 -0
- tigrbl/column/shortcuts.py +89 -0
- tigrbl/column/storage_spec.py +65 -0
- tigrbl/config/__init__.py +19 -0
- tigrbl/config/constants.py +224 -0
- tigrbl/config/defaults.py +29 -0
- tigrbl/config/resolver.py +295 -0
- tigrbl/core/__init__.py +47 -0
- tigrbl/core/crud/__init__.py +36 -0
- tigrbl/core/crud/bulk.py +168 -0
- tigrbl/core/crud/helpers/__init__.py +76 -0
- tigrbl/core/crud/helpers/db.py +92 -0
- tigrbl/core/crud/helpers/enum.py +86 -0
- tigrbl/core/crud/helpers/filters.py +162 -0
- tigrbl/core/crud/helpers/model.py +123 -0
- tigrbl/core/crud/helpers/normalize.py +99 -0
- tigrbl/core/crud/ops.py +235 -0
- tigrbl/ddl/__init__.py +344 -0
- tigrbl/decorators.py +17 -0
- tigrbl/deps/__init__.py +20 -0
- tigrbl/deps/fastapi.py +45 -0
- tigrbl/deps/favicon.svg +4 -0
- tigrbl/deps/jinja.py +27 -0
- tigrbl/deps/pydantic.py +10 -0
- tigrbl/deps/sqlalchemy.py +94 -0
- tigrbl/deps/starlette.py +36 -0
- tigrbl/engine/__init__.py +45 -0
- tigrbl/engine/_engine.py +144 -0
- tigrbl/engine/bind.py +33 -0
- tigrbl/engine/builders.py +236 -0
- tigrbl/engine/capabilities.py +29 -0
- tigrbl/engine/collect.py +111 -0
- tigrbl/engine/decorators.py +110 -0
- tigrbl/engine/docs/PLUGINS.md +49 -0
- tigrbl/engine/engine_spec.py +355 -0
- tigrbl/engine/plugins.py +52 -0
- tigrbl/engine/registry.py +36 -0
- tigrbl/engine/resolver.py +224 -0
- tigrbl/engine/shortcuts.py +216 -0
- tigrbl/hook/__init__.py +21 -0
- tigrbl/hook/_hook.py +22 -0
- tigrbl/hook/decorators.py +28 -0
- tigrbl/hook/hook_spec.py +24 -0
- tigrbl/hook/mro_collect.py +98 -0
- tigrbl/hook/shortcuts.py +44 -0
- tigrbl/hook/types.py +76 -0
- tigrbl/op/__init__.py +50 -0
- tigrbl/op/_op.py +31 -0
- tigrbl/op/canonical.py +31 -0
- tigrbl/op/collect.py +11 -0
- tigrbl/op/decorators.py +238 -0
- tigrbl/op/model_registry.py +301 -0
- tigrbl/op/mro_collect.py +99 -0
- tigrbl/op/resolver.py +216 -0
- tigrbl/op/types.py +136 -0
- tigrbl/orm/__init__.py +1 -0
- tigrbl/orm/mixins/_RowBound.py +83 -0
- tigrbl/orm/mixins/__init__.py +95 -0
- tigrbl/orm/mixins/bootstrappable.py +113 -0
- tigrbl/orm/mixins/bound.py +47 -0
- tigrbl/orm/mixins/edges.py +40 -0
- tigrbl/orm/mixins/fields.py +165 -0
- tigrbl/orm/mixins/hierarchy.py +54 -0
- tigrbl/orm/mixins/key_digest.py +44 -0
- tigrbl/orm/mixins/lifecycle.py +115 -0
- tigrbl/orm/mixins/locks.py +51 -0
- tigrbl/orm/mixins/markers.py +16 -0
- tigrbl/orm/mixins/operations.py +57 -0
- tigrbl/orm/mixins/ownable.py +337 -0
- tigrbl/orm/mixins/principals.py +98 -0
- tigrbl/orm/mixins/tenant_bound.py +301 -0
- tigrbl/orm/mixins/upsertable.py +118 -0
- tigrbl/orm/mixins/utils.py +49 -0
- tigrbl/orm/tables/__init__.py +72 -0
- tigrbl/orm/tables/_base.py +8 -0
- tigrbl/orm/tables/audit.py +56 -0
- tigrbl/orm/tables/client.py +25 -0
- tigrbl/orm/tables/group.py +29 -0
- tigrbl/orm/tables/org.py +30 -0
- tigrbl/orm/tables/rbac.py +76 -0
- tigrbl/orm/tables/status.py +106 -0
- tigrbl/orm/tables/tenant.py +22 -0
- tigrbl/orm/tables/user.py +39 -0
- tigrbl/response/README.md +34 -0
- tigrbl/response/__init__.py +33 -0
- tigrbl/response/bind.py +12 -0
- tigrbl/response/decorators.py +37 -0
- tigrbl/response/resolver.py +83 -0
- tigrbl/response/shortcuts.py +171 -0
- tigrbl/response/types.py +49 -0
- tigrbl/rest/__init__.py +27 -0
- tigrbl/runtime/README.md +129 -0
- tigrbl/runtime/__init__.py +20 -0
- tigrbl/runtime/atoms/__init__.py +102 -0
- tigrbl/runtime/atoms/emit/__init__.py +42 -0
- tigrbl/runtime/atoms/emit/paired_post.py +158 -0
- tigrbl/runtime/atoms/emit/paired_pre.py +106 -0
- tigrbl/runtime/atoms/emit/readtime_alias.py +120 -0
- tigrbl/runtime/atoms/out/__init__.py +38 -0
- tigrbl/runtime/atoms/out/masking.py +135 -0
- tigrbl/runtime/atoms/refresh/__init__.py +38 -0
- tigrbl/runtime/atoms/refresh/demand.py +130 -0
- tigrbl/runtime/atoms/resolve/__init__.py +40 -0
- tigrbl/runtime/atoms/resolve/assemble.py +167 -0
- tigrbl/runtime/atoms/resolve/paired_gen.py +147 -0
- tigrbl/runtime/atoms/response/__init__.py +19 -0
- tigrbl/runtime/atoms/response/headers_from_payload.py +57 -0
- tigrbl/runtime/atoms/response/negotiate.py +30 -0
- tigrbl/runtime/atoms/response/negotiation.py +43 -0
- tigrbl/runtime/atoms/response/render.py +36 -0
- tigrbl/runtime/atoms/response/renderer.py +116 -0
- tigrbl/runtime/atoms/response/template.py +44 -0
- tigrbl/runtime/atoms/response/templates.py +88 -0
- tigrbl/runtime/atoms/schema/__init__.py +40 -0
- tigrbl/runtime/atoms/schema/collect_in.py +21 -0
- tigrbl/runtime/atoms/schema/collect_out.py +21 -0
- tigrbl/runtime/atoms/storage/__init__.py +38 -0
- tigrbl/runtime/atoms/storage/to_stored.py +167 -0
- tigrbl/runtime/atoms/wire/__init__.py +45 -0
- tigrbl/runtime/atoms/wire/build_in.py +166 -0
- tigrbl/runtime/atoms/wire/build_out.py +87 -0
- tigrbl/runtime/atoms/wire/dump.py +206 -0
- tigrbl/runtime/atoms/wire/validate_in.py +227 -0
- tigrbl/runtime/context.py +206 -0
- tigrbl/runtime/errors/__init__.py +61 -0
- tigrbl/runtime/errors/converters.py +214 -0
- tigrbl/runtime/errors/exceptions.py +124 -0
- tigrbl/runtime/errors/mappings.py +71 -0
- tigrbl/runtime/errors/utils.py +150 -0
- tigrbl/runtime/events.py +209 -0
- tigrbl/runtime/executor/__init__.py +6 -0
- tigrbl/runtime/executor/guards.py +132 -0
- tigrbl/runtime/executor/helpers.py +88 -0
- tigrbl/runtime/executor/invoke.py +150 -0
- tigrbl/runtime/executor/types.py +84 -0
- tigrbl/runtime/kernel.py +644 -0
- tigrbl/runtime/labels.py +353 -0
- tigrbl/runtime/opview.py +89 -0
- tigrbl/runtime/ordering.py +256 -0
- tigrbl/runtime/system.py +279 -0
- tigrbl/runtime/trace.py +330 -0
- tigrbl/schema/__init__.py +38 -0
- tigrbl/schema/_schema.py +27 -0
- tigrbl/schema/builder/__init__.py +17 -0
- tigrbl/schema/builder/build_schema.py +209 -0
- tigrbl/schema/builder/cache.py +24 -0
- tigrbl/schema/builder/compat.py +16 -0
- tigrbl/schema/builder/extras.py +85 -0
- tigrbl/schema/builder/helpers.py +51 -0
- tigrbl/schema/builder/list_params.py +117 -0
- tigrbl/schema/builder/strip_parent_fields.py +70 -0
- tigrbl/schema/collect.py +79 -0
- tigrbl/schema/decorators.py +68 -0
- tigrbl/schema/get_schema.py +86 -0
- tigrbl/schema/schema_spec.py +20 -0
- tigrbl/schema/shortcuts.py +42 -0
- tigrbl/schema/types.py +34 -0
- tigrbl/schema/utils.py +143 -0
- tigrbl/session/README.md +14 -0
- tigrbl/session/__init__.py +28 -0
- tigrbl/session/abc.py +76 -0
- tigrbl/session/base.py +151 -0
- tigrbl/session/decorators.py +43 -0
- tigrbl/session/default.py +118 -0
- tigrbl/session/shortcuts.py +50 -0
- tigrbl/session/spec.py +112 -0
- tigrbl/shortcuts.py +22 -0
- tigrbl/specs.py +44 -0
- tigrbl/system/__init__.py +13 -0
- tigrbl/system/diagnostics/__init__.py +24 -0
- tigrbl/system/diagnostics/compat.py +31 -0
- tigrbl/system/diagnostics/healthz.py +41 -0
- tigrbl/system/diagnostics/hookz.py +51 -0
- tigrbl/system/diagnostics/kernelz.py +20 -0
- tigrbl/system/diagnostics/methodz.py +43 -0
- tigrbl/system/diagnostics/router.py +73 -0
- tigrbl/system/diagnostics/utils.py +43 -0
- tigrbl/system/uvicorn.py +60 -0
- tigrbl/table/__init__.py +9 -0
- tigrbl/table/_base.py +260 -0
- tigrbl/table/_table.py +54 -0
- tigrbl/table/mro_collect.py +69 -0
- tigrbl/table/shortcuts.py +57 -0
- tigrbl/table/table_spec.py +28 -0
- tigrbl/transport/__init__.py +74 -0
- tigrbl/transport/jsonrpc/__init__.py +19 -0
- tigrbl/transport/jsonrpc/dispatcher.py +352 -0
- tigrbl/transport/jsonrpc/helpers.py +115 -0
- tigrbl/transport/jsonrpc/models.py +41 -0
- tigrbl/transport/rest/__init__.py +25 -0
- tigrbl/transport/rest/aggregator.py +132 -0
- tigrbl/types/__init__.py +170 -0
- tigrbl/types/allow_anon_provider.py +19 -0
- tigrbl/types/authn_abc.py +30 -0
- tigrbl/types/nested_path_provider.py +22 -0
- tigrbl/types/op.py +35 -0
- tigrbl/types/op_config_provider.py +17 -0
- tigrbl/types/op_verb_alias_provider.py +33 -0
- tigrbl/types/request_extras_provider.py +22 -0
- tigrbl/types/response_extras_provider.py +22 -0
- tigrbl/types/table_config_provider.py +13 -0
- tigrbl/types/uuid.py +55 -0
- tigrbl-0.3.0.dist-info/METADATA +516 -0
- tigrbl-0.3.0.dist-info/RECORD +266 -0
- {tigrbl-0.0.1.dev1.dist-info → tigrbl-0.3.0.dist-info}/WHEEL +1 -1
- tigrbl-0.3.0.dist-info/licenses/LICENSE +201 -0
- tigrbl/ExampleAgent.py +0 -1
- tigrbl-0.0.1.dev1.dist-info/METADATA +0 -18
- tigrbl-0.0.1.dev1.dist-info/RECORD +0 -5
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# tigrbl/v3/runtime/executor/invoke.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Any, MutableMapping, Optional, Union
|
|
6
|
+
|
|
7
|
+
from .types import _Ctx, PhaseChains, Request, Session, AsyncSession
|
|
8
|
+
from .helpers import _in_tx, _run_chain, _g
|
|
9
|
+
from .guards import _install_db_guards, _rollback_if_owned
|
|
10
|
+
from ..errors import create_standardized_error
|
|
11
|
+
from ...config.constants import CTX_SKIP_PERSIST_FLAG
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def _invoke(
|
|
17
|
+
*,
|
|
18
|
+
request: Optional[Request],
|
|
19
|
+
db: Union[Session, AsyncSession, None],
|
|
20
|
+
phases: Optional[PhaseChains],
|
|
21
|
+
ctx: Optional[MutableMapping[str, Any]] = None,
|
|
22
|
+
) -> Any:
|
|
23
|
+
"""Execute an operation through explicit phases with strict write policies."""
|
|
24
|
+
|
|
25
|
+
ctx = _Ctx.ensure(request=request, db=db, seed=ctx)
|
|
26
|
+
if getattr(ctx, "app", None) is None and getattr(ctx, "api", None) is not None:
|
|
27
|
+
ctx.app = ctx.api
|
|
28
|
+
if getattr(ctx, "op", None) is None and getattr(ctx, "method", None) is not None:
|
|
29
|
+
ctx.op = ctx.method
|
|
30
|
+
if getattr(ctx, "model", None) is None:
|
|
31
|
+
obj = getattr(ctx, "obj", None)
|
|
32
|
+
if obj is not None:
|
|
33
|
+
ctx.model = type(obj)
|
|
34
|
+
skip_persist: bool = bool(ctx.get(CTX_SKIP_PERSIST_FLAG) or ctx.get("skip_persist"))
|
|
35
|
+
|
|
36
|
+
existed_tx_before = _in_tx(db) if db is not None else False
|
|
37
|
+
|
|
38
|
+
async def _run_phase(
|
|
39
|
+
name: str,
|
|
40
|
+
*,
|
|
41
|
+
allow_flush: bool,
|
|
42
|
+
allow_commit: bool,
|
|
43
|
+
in_tx: bool,
|
|
44
|
+
require_owned_for_commit: bool = True,
|
|
45
|
+
nonfatal: bool = False,
|
|
46
|
+
owns_tx_for_phase: Optional[bool] = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
chain = _g(phases, name)
|
|
49
|
+
if not chain:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
eff_allow_flush = allow_flush and (not skip_persist)
|
|
53
|
+
eff_allow_commit = allow_commit and (not skip_persist)
|
|
54
|
+
|
|
55
|
+
owns_tx_now = bool(owns_tx_for_phase)
|
|
56
|
+
if owns_tx_for_phase is None:
|
|
57
|
+
owns_tx_now = not existed_tx_before
|
|
58
|
+
|
|
59
|
+
guard = _install_db_guards(
|
|
60
|
+
db,
|
|
61
|
+
phase=name,
|
|
62
|
+
allow_flush=eff_allow_flush,
|
|
63
|
+
allow_commit=eff_allow_commit,
|
|
64
|
+
require_owned_tx_for_commit=require_owned_for_commit,
|
|
65
|
+
owns_tx=owns_tx_now,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
await _run_chain(ctx, chain, phase=name)
|
|
70
|
+
except Exception as exc:
|
|
71
|
+
ctx.error = exc
|
|
72
|
+
if in_tx:
|
|
73
|
+
await _rollback_if_owned(db, owns_tx_now, phases=phases, ctx=ctx)
|
|
74
|
+
err_name = f"ON_{name}_ERROR"
|
|
75
|
+
try:
|
|
76
|
+
await _run_chain(
|
|
77
|
+
ctx, _g(phases, err_name) or _g(phases, "ON_ERROR"), phase=err_name
|
|
78
|
+
)
|
|
79
|
+
except Exception: # pragma: no cover
|
|
80
|
+
pass
|
|
81
|
+
if nonfatal:
|
|
82
|
+
logger.exception("%s failed (nonfatal): %s", name, exc)
|
|
83
|
+
return
|
|
84
|
+
raise create_standardized_error(exc)
|
|
85
|
+
finally:
|
|
86
|
+
guard.restore()
|
|
87
|
+
|
|
88
|
+
await _run_phase("PRE_TX_BEGIN", allow_flush=False, allow_commit=False, in_tx=False)
|
|
89
|
+
|
|
90
|
+
if not skip_persist:
|
|
91
|
+
await _run_phase(
|
|
92
|
+
"START_TX",
|
|
93
|
+
allow_flush=False,
|
|
94
|
+
allow_commit=False,
|
|
95
|
+
in_tx=False,
|
|
96
|
+
require_owned_for_commit=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
await _run_phase(
|
|
100
|
+
"PRE_HANDLER", allow_flush=True, allow_commit=False, in_tx=not skip_persist
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
await _run_phase(
|
|
104
|
+
"HANDLER", allow_flush=True, allow_commit=False, in_tx=not skip_persist
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
await _run_phase(
|
|
108
|
+
"POST_HANDLER", allow_flush=True, allow_commit=False, in_tx=not skip_persist
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
await _run_phase(
|
|
112
|
+
"PRE_COMMIT", allow_flush=False, allow_commit=False, in_tx=not skip_persist
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if not skip_persist:
|
|
116
|
+
owns_tx_for_commit = (not existed_tx_before) and (db is not None and _in_tx(db))
|
|
117
|
+
await _run_phase(
|
|
118
|
+
"END_TX",
|
|
119
|
+
allow_flush=True,
|
|
120
|
+
allow_commit=True,
|
|
121
|
+
in_tx=True,
|
|
122
|
+
require_owned_for_commit=True,
|
|
123
|
+
owns_tx_for_phase=owns_tx_for_commit,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
from types import SimpleNamespace as _NS
|
|
127
|
+
|
|
128
|
+
serializer = ctx.get("response_serializer")
|
|
129
|
+
if callable(serializer):
|
|
130
|
+
try:
|
|
131
|
+
ctx["result"] = serializer(ctx.get("result"))
|
|
132
|
+
except Exception:
|
|
133
|
+
logger.exception("response serialization failed", exc_info=True)
|
|
134
|
+
ctx.response = _NS(result=ctx.get("result"))
|
|
135
|
+
|
|
136
|
+
await _run_phase("POST_COMMIT", allow_flush=True, allow_commit=False, in_tx=False)
|
|
137
|
+
|
|
138
|
+
await _run_phase(
|
|
139
|
+
"POST_RESPONSE",
|
|
140
|
+
allow_flush=False,
|
|
141
|
+
allow_commit=False,
|
|
142
|
+
in_tx=False,
|
|
143
|
+
nonfatal=True,
|
|
144
|
+
)
|
|
145
|
+
if ctx.get("result") is not None:
|
|
146
|
+
ctx.response.result = ctx.get("result")
|
|
147
|
+
return ctx.response.result
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
__all__ = ["_invoke"]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# tigrbl/v3/runtime/executor/types.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Awaitable,
|
|
7
|
+
Callable,
|
|
8
|
+
Mapping,
|
|
9
|
+
MutableMapping,
|
|
10
|
+
Optional,
|
|
11
|
+
Sequence,
|
|
12
|
+
Union,
|
|
13
|
+
Protocol,
|
|
14
|
+
runtime_checkable,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from fastapi import Request # type: ignore
|
|
19
|
+
except Exception: # pragma: no cover
|
|
20
|
+
Request = Any # type: ignore
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from sqlalchemy.orm import Session # type: ignore
|
|
24
|
+
from sqlalchemy.ext.asyncio import AsyncSession # type: ignore
|
|
25
|
+
except Exception: # pragma: no cover
|
|
26
|
+
Session = Any # type: ignore
|
|
27
|
+
AsyncSession = Any # type: ignore
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@runtime_checkable
|
|
31
|
+
class _Step(Protocol):
|
|
32
|
+
def __call__(self, ctx: "_Ctx") -> Union[Any, Awaitable[Any]]: ...
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
HandlerStep = Union[
|
|
36
|
+
_Step,
|
|
37
|
+
Callable[["_Ctx"], Any],
|
|
38
|
+
Callable[["_Ctx"], Awaitable[Any]],
|
|
39
|
+
]
|
|
40
|
+
PhaseChains = Mapping[str, Sequence[HandlerStep]]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class _Ctx(dict):
|
|
44
|
+
"""Dict-like context with attribute access.
|
|
45
|
+
|
|
46
|
+
Common keys:
|
|
47
|
+
• request: FastAPI Request (optional)
|
|
48
|
+
• db: Session | AsyncSession
|
|
49
|
+
• api/model/op: optional metadata
|
|
50
|
+
• result: last non-None step result
|
|
51
|
+
• error: last exception caught (on failure paths)
|
|
52
|
+
• response: SimpleNamespace(result=...) for POST_RESPONSE shaping
|
|
53
|
+
• temp: scratch dict used by atoms/hook steps
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
__slots__ = ()
|
|
57
|
+
__getattr__ = dict.get # type: ignore[assignment]
|
|
58
|
+
__setattr__ = dict.__setitem__ # type: ignore[assignment]
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def ensure(
|
|
62
|
+
cls,
|
|
63
|
+
*,
|
|
64
|
+
request: Optional[Request],
|
|
65
|
+
db: Union[Session, AsyncSession, None],
|
|
66
|
+
seed: Optional[MutableMapping[str, Any]] = None,
|
|
67
|
+
) -> "_Ctx":
|
|
68
|
+
ctx = cls() if seed is None else (seed if isinstance(seed, _Ctx) else cls(seed))
|
|
69
|
+
if request is not None:
|
|
70
|
+
ctx.request = request
|
|
71
|
+
state = getattr(request, "state", None)
|
|
72
|
+
if state is not None and getattr(state, "ctx", None) is None:
|
|
73
|
+
try:
|
|
74
|
+
state.ctx = ctx # make ctx available to deps
|
|
75
|
+
except Exception: # pragma: no cover
|
|
76
|
+
pass
|
|
77
|
+
if db is not None:
|
|
78
|
+
ctx.db = db
|
|
79
|
+
if "temp" not in ctx or not isinstance(ctx.get("temp"), dict):
|
|
80
|
+
ctx.temp = {}
|
|
81
|
+
return ctx
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
__all__ = ["_Ctx", "HandlerStep", "PhaseChains", "Request", "Session", "AsyncSession"]
|