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.
Files changed (269) hide show
  1. tigrbl/README.md +94 -0
  2. tigrbl/__init__.py +139 -14
  3. tigrbl/api/__init__.py +6 -0
  4. tigrbl/api/_api.py +97 -0
  5. tigrbl/api/api_spec.py +30 -0
  6. tigrbl/api/mro_collect.py +43 -0
  7. tigrbl/api/shortcuts.py +56 -0
  8. tigrbl/api/tigrbl_api.py +291 -0
  9. tigrbl/app/__init__.py +0 -0
  10. tigrbl/app/_app.py +86 -0
  11. tigrbl/app/_model_registry.py +41 -0
  12. tigrbl/app/app_spec.py +42 -0
  13. tigrbl/app/mro_collect.py +67 -0
  14. tigrbl/app/shortcuts.py +65 -0
  15. tigrbl/app/tigrbl_app.py +319 -0
  16. tigrbl/bindings/__init__.py +73 -0
  17. tigrbl/bindings/api/__init__.py +12 -0
  18. tigrbl/bindings/api/common.py +109 -0
  19. tigrbl/bindings/api/include.py +256 -0
  20. tigrbl/bindings/api/resource_proxy.py +149 -0
  21. tigrbl/bindings/api/rpc.py +111 -0
  22. tigrbl/bindings/columns.py +49 -0
  23. tigrbl/bindings/handlers/__init__.py +11 -0
  24. tigrbl/bindings/handlers/builder.py +119 -0
  25. tigrbl/bindings/handlers/ctx.py +74 -0
  26. tigrbl/bindings/handlers/identifiers.py +228 -0
  27. tigrbl/bindings/handlers/namespaces.py +51 -0
  28. tigrbl/bindings/handlers/steps.py +276 -0
  29. tigrbl/bindings/hooks.py +311 -0
  30. tigrbl/bindings/model.py +194 -0
  31. tigrbl/bindings/model_helpers.py +139 -0
  32. tigrbl/bindings/model_registry.py +77 -0
  33. tigrbl/bindings/rest/__init__.py +7 -0
  34. tigrbl/bindings/rest/attach.py +34 -0
  35. tigrbl/bindings/rest/collection.py +286 -0
  36. tigrbl/bindings/rest/common.py +120 -0
  37. tigrbl/bindings/rest/fastapi.py +76 -0
  38. tigrbl/bindings/rest/helpers.py +119 -0
  39. tigrbl/bindings/rest/io.py +317 -0
  40. tigrbl/bindings/rest/io_headers.py +49 -0
  41. tigrbl/bindings/rest/member.py +386 -0
  42. tigrbl/bindings/rest/router.py +296 -0
  43. tigrbl/bindings/rest/routing.py +153 -0
  44. tigrbl/bindings/rpc.py +364 -0
  45. tigrbl/bindings/schemas/__init__.py +11 -0
  46. tigrbl/bindings/schemas/builder.py +348 -0
  47. tigrbl/bindings/schemas/defaults.py +260 -0
  48. tigrbl/bindings/schemas/utils.py +193 -0
  49. tigrbl/column/README.md +62 -0
  50. tigrbl/column/__init__.py +72 -0
  51. tigrbl/column/_column.py +96 -0
  52. tigrbl/column/column_spec.py +40 -0
  53. tigrbl/column/field_spec.py +31 -0
  54. tigrbl/column/infer/__init__.py +25 -0
  55. tigrbl/column/infer/core.py +92 -0
  56. tigrbl/column/infer/jsonhints.py +44 -0
  57. tigrbl/column/infer/planning.py +133 -0
  58. tigrbl/column/infer/types.py +102 -0
  59. tigrbl/column/infer/utils.py +59 -0
  60. tigrbl/column/io_spec.py +136 -0
  61. tigrbl/column/mro_collect.py +59 -0
  62. tigrbl/column/shortcuts.py +89 -0
  63. tigrbl/column/storage_spec.py +65 -0
  64. tigrbl/config/__init__.py +19 -0
  65. tigrbl/config/constants.py +224 -0
  66. tigrbl/config/defaults.py +29 -0
  67. tigrbl/config/resolver.py +295 -0
  68. tigrbl/core/__init__.py +47 -0
  69. tigrbl/core/crud/__init__.py +36 -0
  70. tigrbl/core/crud/bulk.py +168 -0
  71. tigrbl/core/crud/helpers/__init__.py +76 -0
  72. tigrbl/core/crud/helpers/db.py +92 -0
  73. tigrbl/core/crud/helpers/enum.py +86 -0
  74. tigrbl/core/crud/helpers/filters.py +162 -0
  75. tigrbl/core/crud/helpers/model.py +123 -0
  76. tigrbl/core/crud/helpers/normalize.py +99 -0
  77. tigrbl/core/crud/ops.py +235 -0
  78. tigrbl/ddl/__init__.py +344 -0
  79. tigrbl/decorators.py +17 -0
  80. tigrbl/deps/__init__.py +20 -0
  81. tigrbl/deps/fastapi.py +45 -0
  82. tigrbl/deps/favicon.svg +4 -0
  83. tigrbl/deps/jinja.py +27 -0
  84. tigrbl/deps/pydantic.py +10 -0
  85. tigrbl/deps/sqlalchemy.py +94 -0
  86. tigrbl/deps/starlette.py +36 -0
  87. tigrbl/engine/__init__.py +45 -0
  88. tigrbl/engine/_engine.py +144 -0
  89. tigrbl/engine/bind.py +33 -0
  90. tigrbl/engine/builders.py +236 -0
  91. tigrbl/engine/capabilities.py +29 -0
  92. tigrbl/engine/collect.py +111 -0
  93. tigrbl/engine/decorators.py +110 -0
  94. tigrbl/engine/docs/PLUGINS.md +49 -0
  95. tigrbl/engine/engine_spec.py +355 -0
  96. tigrbl/engine/plugins.py +52 -0
  97. tigrbl/engine/registry.py +36 -0
  98. tigrbl/engine/resolver.py +224 -0
  99. tigrbl/engine/shortcuts.py +216 -0
  100. tigrbl/hook/__init__.py +21 -0
  101. tigrbl/hook/_hook.py +22 -0
  102. tigrbl/hook/decorators.py +28 -0
  103. tigrbl/hook/hook_spec.py +24 -0
  104. tigrbl/hook/mro_collect.py +98 -0
  105. tigrbl/hook/shortcuts.py +44 -0
  106. tigrbl/hook/types.py +76 -0
  107. tigrbl/op/__init__.py +50 -0
  108. tigrbl/op/_op.py +31 -0
  109. tigrbl/op/canonical.py +31 -0
  110. tigrbl/op/collect.py +11 -0
  111. tigrbl/op/decorators.py +238 -0
  112. tigrbl/op/model_registry.py +301 -0
  113. tigrbl/op/mro_collect.py +99 -0
  114. tigrbl/op/resolver.py +216 -0
  115. tigrbl/op/types.py +136 -0
  116. tigrbl/orm/__init__.py +1 -0
  117. tigrbl/orm/mixins/_RowBound.py +83 -0
  118. tigrbl/orm/mixins/__init__.py +95 -0
  119. tigrbl/orm/mixins/bootstrappable.py +113 -0
  120. tigrbl/orm/mixins/bound.py +47 -0
  121. tigrbl/orm/mixins/edges.py +40 -0
  122. tigrbl/orm/mixins/fields.py +165 -0
  123. tigrbl/orm/mixins/hierarchy.py +54 -0
  124. tigrbl/orm/mixins/key_digest.py +44 -0
  125. tigrbl/orm/mixins/lifecycle.py +115 -0
  126. tigrbl/orm/mixins/locks.py +51 -0
  127. tigrbl/orm/mixins/markers.py +16 -0
  128. tigrbl/orm/mixins/operations.py +57 -0
  129. tigrbl/orm/mixins/ownable.py +337 -0
  130. tigrbl/orm/mixins/principals.py +98 -0
  131. tigrbl/orm/mixins/tenant_bound.py +301 -0
  132. tigrbl/orm/mixins/upsertable.py +118 -0
  133. tigrbl/orm/mixins/utils.py +49 -0
  134. tigrbl/orm/tables/__init__.py +72 -0
  135. tigrbl/orm/tables/_base.py +8 -0
  136. tigrbl/orm/tables/audit.py +56 -0
  137. tigrbl/orm/tables/client.py +25 -0
  138. tigrbl/orm/tables/group.py +29 -0
  139. tigrbl/orm/tables/org.py +30 -0
  140. tigrbl/orm/tables/rbac.py +76 -0
  141. tigrbl/orm/tables/status.py +106 -0
  142. tigrbl/orm/tables/tenant.py +22 -0
  143. tigrbl/orm/tables/user.py +39 -0
  144. tigrbl/response/README.md +34 -0
  145. tigrbl/response/__init__.py +33 -0
  146. tigrbl/response/bind.py +12 -0
  147. tigrbl/response/decorators.py +37 -0
  148. tigrbl/response/resolver.py +83 -0
  149. tigrbl/response/shortcuts.py +171 -0
  150. tigrbl/response/types.py +49 -0
  151. tigrbl/rest/__init__.py +27 -0
  152. tigrbl/runtime/README.md +129 -0
  153. tigrbl/runtime/__init__.py +20 -0
  154. tigrbl/runtime/atoms/__init__.py +102 -0
  155. tigrbl/runtime/atoms/emit/__init__.py +42 -0
  156. tigrbl/runtime/atoms/emit/paired_post.py +158 -0
  157. tigrbl/runtime/atoms/emit/paired_pre.py +106 -0
  158. tigrbl/runtime/atoms/emit/readtime_alias.py +120 -0
  159. tigrbl/runtime/atoms/out/__init__.py +38 -0
  160. tigrbl/runtime/atoms/out/masking.py +135 -0
  161. tigrbl/runtime/atoms/refresh/__init__.py +38 -0
  162. tigrbl/runtime/atoms/refresh/demand.py +130 -0
  163. tigrbl/runtime/atoms/resolve/__init__.py +40 -0
  164. tigrbl/runtime/atoms/resolve/assemble.py +167 -0
  165. tigrbl/runtime/atoms/resolve/paired_gen.py +147 -0
  166. tigrbl/runtime/atoms/response/__init__.py +19 -0
  167. tigrbl/runtime/atoms/response/headers_from_payload.py +57 -0
  168. tigrbl/runtime/atoms/response/negotiate.py +30 -0
  169. tigrbl/runtime/atoms/response/negotiation.py +43 -0
  170. tigrbl/runtime/atoms/response/render.py +36 -0
  171. tigrbl/runtime/atoms/response/renderer.py +116 -0
  172. tigrbl/runtime/atoms/response/template.py +44 -0
  173. tigrbl/runtime/atoms/response/templates.py +88 -0
  174. tigrbl/runtime/atoms/schema/__init__.py +40 -0
  175. tigrbl/runtime/atoms/schema/collect_in.py +21 -0
  176. tigrbl/runtime/atoms/schema/collect_out.py +21 -0
  177. tigrbl/runtime/atoms/storage/__init__.py +38 -0
  178. tigrbl/runtime/atoms/storage/to_stored.py +167 -0
  179. tigrbl/runtime/atoms/wire/__init__.py +45 -0
  180. tigrbl/runtime/atoms/wire/build_in.py +166 -0
  181. tigrbl/runtime/atoms/wire/build_out.py +87 -0
  182. tigrbl/runtime/atoms/wire/dump.py +206 -0
  183. tigrbl/runtime/atoms/wire/validate_in.py +227 -0
  184. tigrbl/runtime/context.py +206 -0
  185. tigrbl/runtime/errors/__init__.py +61 -0
  186. tigrbl/runtime/errors/converters.py +214 -0
  187. tigrbl/runtime/errors/exceptions.py +124 -0
  188. tigrbl/runtime/errors/mappings.py +71 -0
  189. tigrbl/runtime/errors/utils.py +150 -0
  190. tigrbl/runtime/events.py +209 -0
  191. tigrbl/runtime/executor/__init__.py +6 -0
  192. tigrbl/runtime/executor/guards.py +132 -0
  193. tigrbl/runtime/executor/helpers.py +88 -0
  194. tigrbl/runtime/executor/invoke.py +150 -0
  195. tigrbl/runtime/executor/types.py +84 -0
  196. tigrbl/runtime/kernel.py +644 -0
  197. tigrbl/runtime/labels.py +353 -0
  198. tigrbl/runtime/opview.py +89 -0
  199. tigrbl/runtime/ordering.py +256 -0
  200. tigrbl/runtime/system.py +279 -0
  201. tigrbl/runtime/trace.py +330 -0
  202. tigrbl/schema/__init__.py +38 -0
  203. tigrbl/schema/_schema.py +27 -0
  204. tigrbl/schema/builder/__init__.py +17 -0
  205. tigrbl/schema/builder/build_schema.py +209 -0
  206. tigrbl/schema/builder/cache.py +24 -0
  207. tigrbl/schema/builder/compat.py +16 -0
  208. tigrbl/schema/builder/extras.py +85 -0
  209. tigrbl/schema/builder/helpers.py +51 -0
  210. tigrbl/schema/builder/list_params.py +117 -0
  211. tigrbl/schema/builder/strip_parent_fields.py +70 -0
  212. tigrbl/schema/collect.py +79 -0
  213. tigrbl/schema/decorators.py +68 -0
  214. tigrbl/schema/get_schema.py +86 -0
  215. tigrbl/schema/schema_spec.py +20 -0
  216. tigrbl/schema/shortcuts.py +42 -0
  217. tigrbl/schema/types.py +34 -0
  218. tigrbl/schema/utils.py +143 -0
  219. tigrbl/session/README.md +14 -0
  220. tigrbl/session/__init__.py +28 -0
  221. tigrbl/session/abc.py +76 -0
  222. tigrbl/session/base.py +151 -0
  223. tigrbl/session/decorators.py +43 -0
  224. tigrbl/session/default.py +118 -0
  225. tigrbl/session/shortcuts.py +50 -0
  226. tigrbl/session/spec.py +112 -0
  227. tigrbl/shortcuts.py +22 -0
  228. tigrbl/specs.py +44 -0
  229. tigrbl/system/__init__.py +13 -0
  230. tigrbl/system/diagnostics/__init__.py +24 -0
  231. tigrbl/system/diagnostics/compat.py +31 -0
  232. tigrbl/system/diagnostics/healthz.py +41 -0
  233. tigrbl/system/diagnostics/hookz.py +51 -0
  234. tigrbl/system/diagnostics/kernelz.py +20 -0
  235. tigrbl/system/diagnostics/methodz.py +43 -0
  236. tigrbl/system/diagnostics/router.py +73 -0
  237. tigrbl/system/diagnostics/utils.py +43 -0
  238. tigrbl/system/uvicorn.py +60 -0
  239. tigrbl/table/__init__.py +9 -0
  240. tigrbl/table/_base.py +260 -0
  241. tigrbl/table/_table.py +54 -0
  242. tigrbl/table/mro_collect.py +69 -0
  243. tigrbl/table/shortcuts.py +57 -0
  244. tigrbl/table/table_spec.py +28 -0
  245. tigrbl/transport/__init__.py +74 -0
  246. tigrbl/transport/jsonrpc/__init__.py +19 -0
  247. tigrbl/transport/jsonrpc/dispatcher.py +352 -0
  248. tigrbl/transport/jsonrpc/helpers.py +115 -0
  249. tigrbl/transport/jsonrpc/models.py +41 -0
  250. tigrbl/transport/rest/__init__.py +25 -0
  251. tigrbl/transport/rest/aggregator.py +132 -0
  252. tigrbl/types/__init__.py +170 -0
  253. tigrbl/types/allow_anon_provider.py +19 -0
  254. tigrbl/types/authn_abc.py +30 -0
  255. tigrbl/types/nested_path_provider.py +22 -0
  256. tigrbl/types/op.py +35 -0
  257. tigrbl/types/op_config_provider.py +17 -0
  258. tigrbl/types/op_verb_alias_provider.py +33 -0
  259. tigrbl/types/request_extras_provider.py +22 -0
  260. tigrbl/types/response_extras_provider.py +22 -0
  261. tigrbl/types/table_config_provider.py +13 -0
  262. tigrbl/types/uuid.py +55 -0
  263. tigrbl-0.3.0.dist-info/METADATA +516 -0
  264. tigrbl-0.3.0.dist-info/RECORD +266 -0
  265. {tigrbl-0.0.1.dev1.dist-info → tigrbl-0.3.0.dist-info}/WHEEL +1 -1
  266. tigrbl-0.3.0.dist-info/licenses/LICENSE +201 -0
  267. tigrbl/ExampleAgent.py +0 -1
  268. tigrbl-0.0.1.dev1.dist-info/METADATA +0 -18
  269. 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"]