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
tigrbl/hook/types.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Hook type definitions for Tigrbl v3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Awaitable, Callable, Literal, Tuple
|
|
7
|
+
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
# Runtime phases (align with runtime/executor.py)
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PHASE(str, Enum):
|
|
14
|
+
PRE_TX_BEGIN = "PRE_TX_BEGIN"
|
|
15
|
+
START_TX = "START_TX"
|
|
16
|
+
PRE_HANDLER = "PRE_HANDLER"
|
|
17
|
+
HANDLER = "HANDLER"
|
|
18
|
+
POST_HANDLER = "POST_HANDLER"
|
|
19
|
+
PRE_COMMIT = "PRE_COMMIT"
|
|
20
|
+
END_TX = "END_TX"
|
|
21
|
+
POST_COMMIT = "POST_COMMIT"
|
|
22
|
+
POST_RESPONSE = "POST_RESPONSE"
|
|
23
|
+
ON_ERROR = "ON_ERROR"
|
|
24
|
+
ON_PRE_TX_BEGIN_ERROR = "ON_PRE_TX_BEGIN_ERROR"
|
|
25
|
+
ON_START_TX_ERROR = "ON_START_TX_ERROR"
|
|
26
|
+
ON_PRE_HANDLER_ERROR = "ON_PRE_HANDLER_ERROR"
|
|
27
|
+
ON_HANDLER_ERROR = "ON_HANDLER_ERROR"
|
|
28
|
+
ON_POST_HANDLER_ERROR = "ON_POST_HANDLER_ERROR"
|
|
29
|
+
ON_PRE_COMMIT_ERROR = "ON_PRE_COMMIT_ERROR"
|
|
30
|
+
ON_END_TX_ERROR = "ON_END_TX_ERROR"
|
|
31
|
+
ON_POST_COMMIT_ERROR = "ON_POST_COMMIT_ERROR"
|
|
32
|
+
ON_POST_RESPONSE_ERROR = "ON_POST_RESPONSE_ERROR"
|
|
33
|
+
ON_ROLLBACK = "ON_ROLLBACK"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
HookPhase = Literal[
|
|
37
|
+
"PRE_TX_BEGIN",
|
|
38
|
+
"START_TX",
|
|
39
|
+
"PRE_HANDLER",
|
|
40
|
+
"HANDLER",
|
|
41
|
+
"POST_HANDLER",
|
|
42
|
+
"PRE_COMMIT",
|
|
43
|
+
"END_TX",
|
|
44
|
+
"POST_COMMIT",
|
|
45
|
+
"POST_RESPONSE",
|
|
46
|
+
"ON_ERROR",
|
|
47
|
+
"ON_PRE_TX_BEGIN_ERROR",
|
|
48
|
+
"ON_START_TX_ERROR",
|
|
49
|
+
"ON_PRE_HANDLER_ERROR",
|
|
50
|
+
"ON_HANDLER_ERROR",
|
|
51
|
+
"ON_POST_HANDLER_ERROR",
|
|
52
|
+
"ON_PRE_COMMIT_ERROR",
|
|
53
|
+
"ON_END_TX_ERROR",
|
|
54
|
+
"ON_POST_COMMIT_ERROR",
|
|
55
|
+
"ON_POST_RESPONSE_ERROR",
|
|
56
|
+
"ON_ROLLBACK",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
PHASES: Tuple[HookPhase, ...] = tuple(p.value for p in PHASE)
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# Hook function types
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
Ctx = Any
|
|
66
|
+
StepFn = Callable[[Ctx], Awaitable[Any] | Any]
|
|
67
|
+
HookPredicate = Callable[[Any], bool]
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
"PHASE",
|
|
71
|
+
"HookPhase",
|
|
72
|
+
"PHASES",
|
|
73
|
+
"Ctx",
|
|
74
|
+
"StepFn",
|
|
75
|
+
"HookPredicate",
|
|
76
|
+
]
|
tigrbl/op/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Tigrbl v3 op package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._op import Op
|
|
6
|
+
from .types import (
|
|
7
|
+
OpSpec,
|
|
8
|
+
OpHook,
|
|
9
|
+
TargetOp,
|
|
10
|
+
Arity,
|
|
11
|
+
PersistPolicy,
|
|
12
|
+
PHASE,
|
|
13
|
+
HookPhase,
|
|
14
|
+
PHASES,
|
|
15
|
+
VerbAliasPolicy,
|
|
16
|
+
)
|
|
17
|
+
from .resolver import resolve
|
|
18
|
+
from .collect import apply_alias
|
|
19
|
+
from .model_registry import (
|
|
20
|
+
OpspecRegistry,
|
|
21
|
+
get_registry,
|
|
22
|
+
register_ops,
|
|
23
|
+
get_registered_ops,
|
|
24
|
+
clear_registry,
|
|
25
|
+
)
|
|
26
|
+
from .decorators import alias, alias_ctx, op_alias, op_ctx
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Op",
|
|
30
|
+
"OpSpec",
|
|
31
|
+
"OpHook",
|
|
32
|
+
"TargetOp",
|
|
33
|
+
"Arity",
|
|
34
|
+
"PersistPolicy",
|
|
35
|
+
"PHASE",
|
|
36
|
+
"HookPhase",
|
|
37
|
+
"PHASES",
|
|
38
|
+
"VerbAliasPolicy",
|
|
39
|
+
"resolve",
|
|
40
|
+
"apply_alias",
|
|
41
|
+
"OpspecRegistry",
|
|
42
|
+
"get_registry",
|
|
43
|
+
"register_ops",
|
|
44
|
+
"get_registered_ops",
|
|
45
|
+
"clear_registry",
|
|
46
|
+
"alias",
|
|
47
|
+
"alias_ctx",
|
|
48
|
+
"op_alias",
|
|
49
|
+
"op_ctx",
|
|
50
|
+
]
|
tigrbl/op/_op.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# tigrbl/tigrbl/v3/op/_op.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import replace
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .types import OpSpec
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Op(OpSpec):
|
|
11
|
+
"""Declarative operation descriptor with optional engine binding."""
|
|
12
|
+
|
|
13
|
+
__slots__ = ()
|
|
14
|
+
|
|
15
|
+
def __set_name__(self, owner: type, name: str) -> None: # noqa: D401
|
|
16
|
+
spec = self
|
|
17
|
+
alias = self.alias or name
|
|
18
|
+
if self.table is not owner or self.alias != alias:
|
|
19
|
+
spec = replace(self, table=owner, alias=alias)
|
|
20
|
+
ops = list(getattr(owner, "__tigrbl_ops__", ()) or [])
|
|
21
|
+
ops.append(spec)
|
|
22
|
+
owner.__tigrbl_ops__ = tuple(ops)
|
|
23
|
+
|
|
24
|
+
def install_engines(
|
|
25
|
+
self, *, api: Any | None = None, model: type | None = None
|
|
26
|
+
) -> None:
|
|
27
|
+
from ..engine import install_from_objects
|
|
28
|
+
|
|
29
|
+
m = model if model is not None else self.table
|
|
30
|
+
if m is not None:
|
|
31
|
+
install_from_objects(api=api, models=[m])
|
tigrbl/op/canonical.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Type
|
|
4
|
+
|
|
5
|
+
from ..config.constants import (
|
|
6
|
+
TIGRBL_DEFAULTS_EXCLUDE_ATTR,
|
|
7
|
+
TIGRBL_DEFAULTS_INCLUDE_ATTR,
|
|
8
|
+
TIGRBL_DEFAULTS_MODE_ATTR,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
DEFAULT_CANON_VERBS = {
|
|
12
|
+
"create",
|
|
13
|
+
"read",
|
|
14
|
+
"update",
|
|
15
|
+
"replace",
|
|
16
|
+
"delete",
|
|
17
|
+
"list",
|
|
18
|
+
"clear",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def should_wire_canonical(table: Type, op: str) -> bool:
|
|
23
|
+
mode = getattr(table, TIGRBL_DEFAULTS_MODE_ATTR, "all")
|
|
24
|
+
inc = set(getattr(table, TIGRBL_DEFAULTS_INCLUDE_ATTR, set()))
|
|
25
|
+
exc = set(getattr(table, TIGRBL_DEFAULTS_EXCLUDE_ATTR, set()))
|
|
26
|
+
if mode == "none":
|
|
27
|
+
return False
|
|
28
|
+
if mode == "some":
|
|
29
|
+
return op in inc
|
|
30
|
+
allowed = (DEFAULT_CANON_VERBS | inc) - exc
|
|
31
|
+
return op in allowed # mode == "all"
|
tigrbl/op/collect.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def apply_alias(verb: str, alias_map: Dict[str, str]) -> str:
|
|
7
|
+
"""Resolve canonical verb → alias (falls back to verb)."""
|
|
8
|
+
return alias_map.get(verb, verb)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = ["apply_alias"]
|
tigrbl/op/decorators.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Operation-related decorators for Tigrbl v3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Callable, Iterable, Optional, Sequence, Union
|
|
8
|
+
|
|
9
|
+
from .types import OpSpec, Arity, TargetOp, PersistPolicy
|
|
10
|
+
from ..schema.types import SchemaArg
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Utilities
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _unwrap(obj: Any) -> Callable[..., Any]:
|
|
19
|
+
"""Get underlying function for (class|static)method; else return obj."""
|
|
20
|
+
if isinstance(obj, (classmethod, staticmethod)):
|
|
21
|
+
return obj.__func__ # type: ignore[attr-defined]
|
|
22
|
+
return obj
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ensure_cm(func: Any) -> Any:
|
|
26
|
+
"""Ensure method is a classmethod so it receives (cls, ctx)."""
|
|
27
|
+
if isinstance(func, (classmethod, staticmethod)):
|
|
28
|
+
return func
|
|
29
|
+
return classmethod(func)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _maybe_await(v):
|
|
33
|
+
if inspect.isawaitable(v):
|
|
34
|
+
return v
|
|
35
|
+
|
|
36
|
+
async def _done():
|
|
37
|
+
return v
|
|
38
|
+
|
|
39
|
+
return _done()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# alias_ctx with optional rich overrides
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class AliasDecl:
|
|
49
|
+
alias: str
|
|
50
|
+
# Optional overrides (lazy-capable schema args are fine; resolved later)
|
|
51
|
+
request_schema: Optional[SchemaArg] = None
|
|
52
|
+
response_schema: Optional[SchemaArg] = None
|
|
53
|
+
persist: Optional[PersistPolicy] = None
|
|
54
|
+
arity: Optional[Arity] = None
|
|
55
|
+
rest: Optional[bool] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def alias(name: str, **kw) -> AliasDecl:
|
|
59
|
+
"""Convenience helper: alias('get', response_schema=..., rest=False)."""
|
|
60
|
+
return AliasDecl(alias=name, **kw)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def alias_ctx(**verb_to_alias_or_decl: Union[str, AliasDecl]):
|
|
64
|
+
"""Class decorator mapping canonical verbs → aliases with optional overrides."""
|
|
65
|
+
|
|
66
|
+
def deco(cls: type):
|
|
67
|
+
amap = dict(getattr(cls, "__tigrbl_aliases__", {}) or {})
|
|
68
|
+
overrides = dict(getattr(cls, "__tigrbl_alias_overrides__", {}) or {})
|
|
69
|
+
|
|
70
|
+
for canon, value in verb_to_alias_or_decl.items():
|
|
71
|
+
if isinstance(value, AliasDecl):
|
|
72
|
+
amap[canon] = value.alias
|
|
73
|
+
overrides[canon] = {
|
|
74
|
+
"request_schema": value.request_schema,
|
|
75
|
+
"response_schema": value.response_schema,
|
|
76
|
+
"persist": value.persist,
|
|
77
|
+
"arity": value.arity,
|
|
78
|
+
"rest": value.rest,
|
|
79
|
+
}
|
|
80
|
+
elif isinstance(value, str):
|
|
81
|
+
amap[canon] = value
|
|
82
|
+
else:
|
|
83
|
+
raise TypeError(
|
|
84
|
+
f"alias_ctx[{canon}] must be str or AliasDecl, got {type(value)}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
setattr(cls, "__tigrbl_aliases__", amap)
|
|
88
|
+
setattr(cls, "__tigrbl_alias_overrides__", overrides)
|
|
89
|
+
try: # clear cached alias maps so late-applied decorators take effect
|
|
90
|
+
from .mro_collect import mro_alias_map_for
|
|
91
|
+
|
|
92
|
+
mro_alias_map_for.cache_clear()
|
|
93
|
+
except Exception: # pragma: no cover - best effort
|
|
94
|
+
pass
|
|
95
|
+
return cls
|
|
96
|
+
|
|
97
|
+
return deco
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
# op_alias (class decorator): attach an OpSpec alias to the model
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def op_alias(
|
|
106
|
+
*,
|
|
107
|
+
alias: str,
|
|
108
|
+
target: TargetOp,
|
|
109
|
+
arity: Arity | None = None,
|
|
110
|
+
persist: PersistPolicy = "default",
|
|
111
|
+
request_model: SchemaArg | None = None,
|
|
112
|
+
response_model: SchemaArg | None = None,
|
|
113
|
+
http_methods: Sequence[str] | None = None,
|
|
114
|
+
tags: Sequence[str] | None = None,
|
|
115
|
+
rbac_guard_op: TargetOp | None = None,
|
|
116
|
+
):
|
|
117
|
+
"""Class decorator to declare an alias for an operation."""
|
|
118
|
+
|
|
119
|
+
def deco(table_cls: type):
|
|
120
|
+
ops = list(getattr(table_cls, "__tigrbl_ops__", ()))
|
|
121
|
+
spec = OpSpec(
|
|
122
|
+
alias=alias,
|
|
123
|
+
target=target,
|
|
124
|
+
table=table_cls,
|
|
125
|
+
arity=arity or _infer_arity(target),
|
|
126
|
+
persist=_normalize_persist(persist),
|
|
127
|
+
request_model=request_model,
|
|
128
|
+
response_model=response_model,
|
|
129
|
+
http_methods=tuple(http_methods) if http_methods else None,
|
|
130
|
+
tags=tuple(tags or ()),
|
|
131
|
+
rbac_guard_op=rbac_guard_op,
|
|
132
|
+
)
|
|
133
|
+
ops.append(spec)
|
|
134
|
+
table_cls.__tigrbl_ops__ = tuple(ops)
|
|
135
|
+
return table_cls
|
|
136
|
+
|
|
137
|
+
return deco
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
# op_ctx (single path: target + arity) with schema overrides
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass
|
|
146
|
+
class _OpDecl:
|
|
147
|
+
alias: Optional[str]
|
|
148
|
+
target: Optional[TargetOp]
|
|
149
|
+
arity: Optional[Arity]
|
|
150
|
+
rest: Optional[bool]
|
|
151
|
+
request_schema: Optional[SchemaArg]
|
|
152
|
+
response_schema: Optional[SchemaArg]
|
|
153
|
+
persist: Optional[PersistPolicy]
|
|
154
|
+
status_code: Optional[int]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def op_ctx(
|
|
158
|
+
*,
|
|
159
|
+
bind: Any | Iterable[Any] | None = None,
|
|
160
|
+
alias: Optional[str] = None,
|
|
161
|
+
target: Optional[TargetOp] = None,
|
|
162
|
+
arity: Optional[Arity] = None,
|
|
163
|
+
rest: Optional[bool] = None,
|
|
164
|
+
request_schema: Optional[SchemaArg] = None,
|
|
165
|
+
response_schema: Optional[SchemaArg] = None,
|
|
166
|
+
persist: Optional[PersistPolicy] = None,
|
|
167
|
+
status_code: Optional[int] = None,
|
|
168
|
+
):
|
|
169
|
+
"""Declare a ctx-only operation whose body is `(cls, ctx)`."""
|
|
170
|
+
|
|
171
|
+
def deco(fn):
|
|
172
|
+
cm = _ensure_cm(fn)
|
|
173
|
+
f = _unwrap(cm)
|
|
174
|
+
f.__tigrbl_ctx_only__ = True
|
|
175
|
+
f.__tigrbl_op_decl__ = _OpDecl(
|
|
176
|
+
alias=alias,
|
|
177
|
+
target=target,
|
|
178
|
+
arity=arity,
|
|
179
|
+
rest=rest,
|
|
180
|
+
request_schema=request_schema,
|
|
181
|
+
response_schema=response_schema,
|
|
182
|
+
persist=persist,
|
|
183
|
+
status_code=status_code,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if bind is not None:
|
|
187
|
+
targets = (
|
|
188
|
+
bind
|
|
189
|
+
if isinstance(bind, Iterable) and not isinstance(bind, (str, bytes))
|
|
190
|
+
else [bind]
|
|
191
|
+
)
|
|
192
|
+
for obj in targets:
|
|
193
|
+
setattr(obj, f.__name__, cm)
|
|
194
|
+
|
|
195
|
+
return cm
|
|
196
|
+
|
|
197
|
+
return deco
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
# Collection helpers
|
|
202
|
+
# ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
_COLLECTION_VERBS = {
|
|
205
|
+
"create",
|
|
206
|
+
"list",
|
|
207
|
+
"bulk_create",
|
|
208
|
+
"bulk_update",
|
|
209
|
+
"bulk_replace",
|
|
210
|
+
"bulk_merge",
|
|
211
|
+
"bulk_delete",
|
|
212
|
+
"clear",
|
|
213
|
+
"merge",
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _infer_arity(target: str) -> str:
|
|
218
|
+
return "collection" if target in _COLLECTION_VERBS else "member"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _normalize_persist(p) -> str:
|
|
222
|
+
if p is None:
|
|
223
|
+
return "default"
|
|
224
|
+
p = str(p).lower()
|
|
225
|
+
if p in {"none", "skip", "read"}:
|
|
226
|
+
return "skip"
|
|
227
|
+
if p in {"append"}:
|
|
228
|
+
return "append"
|
|
229
|
+
if p in {"override"}:
|
|
230
|
+
return "override"
|
|
231
|
+
if p in {"prepend"}:
|
|
232
|
+
return "prepend"
|
|
233
|
+
if p in {"write", "default", "persist"}:
|
|
234
|
+
return "default"
|
|
235
|
+
return "default"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
__all__ = ["alias", "alias_ctx", "op_alias", "op_ctx"]
|