tigrbl 0.0.1.dev1__py3-none-any.whl → 0.3.0.dev3__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 +72 -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 +286 -0
- tigrbl/app/__init__.py +0 -0
- tigrbl/app/_app.py +61 -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 +314 -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 +265 -0
- tigrbl/bindings/rest/common.py +116 -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/member.py +367 -0
- tigrbl/bindings/rest/router.py +292 -0
- tigrbl/bindings/rest/routing.py +133 -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 +133 -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 +26 -0
- tigrbl/engine/_engine.py +130 -0
- tigrbl/engine/bind.py +33 -0
- tigrbl/engine/builders.py +236 -0
- tigrbl/engine/collect.py +111 -0
- tigrbl/engine/decorators.py +108 -0
- tigrbl/engine/engine_spec.py +261 -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 +111 -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 +144 -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 +17 -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 +628 -0
- tigrbl/runtime/labels.py +353 -0
- tigrbl/runtime/opview.py +87 -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 +55 -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/shortcuts.py +22 -0
- tigrbl/specs.py +44 -0
- tigrbl/system/__init__.py +12 -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/table/__init__.py +9 -0
- tigrbl/table/_base.py +237 -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 +174 -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-0.3.0.dev3.dist-info/LICENSE +201 -0
- tigrbl-0.3.0.dev3.dist-info/METADATA +501 -0
- tigrbl-0.3.0.dev3.dist-info/RECORD +249 -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-0.0.1.dev1.dist-info → tigrbl-0.3.0.dev3.dist-info}/WHEEL +0 -0
tigrbl/hook/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from ..config.constants import HOOK_DECLS_ATTR
|
|
2
|
+
from .decorators import hook_ctx
|
|
3
|
+
from .types import PHASE, HookPhase, PHASES, Ctx, StepFn, HookPredicate
|
|
4
|
+
from .shortcuts import hook, hook_spec
|
|
5
|
+
from ._hook import Hook
|
|
6
|
+
from .hook_spec import HookSpec
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"hook_ctx",
|
|
10
|
+
"HOOK_DECLS_ATTR",
|
|
11
|
+
"Hook",
|
|
12
|
+
"PHASE",
|
|
13
|
+
"HookPhase",
|
|
14
|
+
"PHASES",
|
|
15
|
+
"Ctx",
|
|
16
|
+
"StepFn",
|
|
17
|
+
"HookPredicate",
|
|
18
|
+
"hook",
|
|
19
|
+
"hook_spec",
|
|
20
|
+
"HookSpec",
|
|
21
|
+
]
|
tigrbl/hook/_hook.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Runtime hook wrapper for Tigrbl v3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Iterable, Optional, Union
|
|
7
|
+
|
|
8
|
+
from .types import HookPhase, StepFn
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class Hook:
|
|
13
|
+
"""Concrete hook bound to a phase and one or more ops."""
|
|
14
|
+
|
|
15
|
+
phase: HookPhase
|
|
16
|
+
fn: StepFn
|
|
17
|
+
ops: Union[str, Iterable[str]]
|
|
18
|
+
name: Optional[str] = None
|
|
19
|
+
description: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["Hook"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Hook-related decorators for Tigrbl v3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable, Union
|
|
6
|
+
|
|
7
|
+
from ..config.constants import HOOK_DECLS_ATTR
|
|
8
|
+
from ._hook import Hook
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def hook_ctx(ops: Union[str, Iterable[str]], *, phase: str):
|
|
12
|
+
"""Declare a ctx-only hook for one/many ops at a given phase."""
|
|
13
|
+
|
|
14
|
+
def deco(fn):
|
|
15
|
+
from ..op.decorators import _ensure_cm, _unwrap
|
|
16
|
+
|
|
17
|
+
cm = _ensure_cm(fn)
|
|
18
|
+
f = _unwrap(cm)
|
|
19
|
+
f.__tigrbl_ctx_only__ = True
|
|
20
|
+
lst = getattr(f, HOOK_DECLS_ATTR, [])
|
|
21
|
+
lst.append(Hook(phase=phase, fn=f, ops=ops))
|
|
22
|
+
setattr(f, HOOK_DECLS_ATTR, lst)
|
|
23
|
+
return cm
|
|
24
|
+
|
|
25
|
+
return deco
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["hook_ctx", "HOOK_DECLS_ATTR"]
|
tigrbl/hook/hook_spec.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Hook specification for Tigrbl v3."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from .types import HookPhase, StepFn, HookPredicate
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class HookSpec:
|
|
13
|
+
phase: HookPhase
|
|
14
|
+
fn: StepFn
|
|
15
|
+
order: int = 0
|
|
16
|
+
when: Optional[HookPredicate] = None
|
|
17
|
+
name: Optional[str] = None
|
|
18
|
+
description: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Backwards compatibility alias
|
|
22
|
+
OpHook = HookSpec
|
|
23
|
+
|
|
24
|
+
__all__ = ["HookSpec", "OpHook"]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Helpers for collecting ctx-only hooks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from typing import Any, Callable, Dict, Iterable, Union
|
|
8
|
+
|
|
9
|
+
from ..runtime.executor import _Ctx
|
|
10
|
+
from ..op.collect import apply_alias
|
|
11
|
+
from ..op.mro_collect import mro_alias_map_for
|
|
12
|
+
from ..op.decorators import _maybe_await, _unwrap
|
|
13
|
+
from .decorators import HOOK_DECLS_ATTR, Hook
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("uvicorn")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _phase_io_key(phase: str) -> str | None:
|
|
19
|
+
p = str(phase)
|
|
20
|
+
if p.startswith("PRE_"):
|
|
21
|
+
return "payload"
|
|
22
|
+
if p.startswith("POST_"):
|
|
23
|
+
return "result"
|
|
24
|
+
if p.startswith("ON_"):
|
|
25
|
+
return "error"
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _wrap_ctx_hook(
|
|
30
|
+
table: type, func: Callable[..., Any], phase: str
|
|
31
|
+
) -> Callable[..., Any]:
|
|
32
|
+
io_key = _phase_io_key(phase)
|
|
33
|
+
|
|
34
|
+
async def hook(
|
|
35
|
+
value=None, *, db=None, request=None, ctx: Dict[str, Any] | None = None
|
|
36
|
+
):
|
|
37
|
+
ctx = _Ctx.ensure(request=request, db=db, seed=ctx)
|
|
38
|
+
if io_key is not None and value is not None:
|
|
39
|
+
ctx[io_key] = value
|
|
40
|
+
bound = func.__get__(table, table)
|
|
41
|
+
_ = await _maybe_await(bound(ctx))
|
|
42
|
+
if io_key is None:
|
|
43
|
+
return None
|
|
44
|
+
return ctx.get(io_key, value)
|
|
45
|
+
|
|
46
|
+
hook.__name__ = getattr(func, "__name__", "hook")
|
|
47
|
+
hook.__qualname__ = getattr(func, "__qualname__", hook.__name__)
|
|
48
|
+
return hook
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@lru_cache(maxsize=None)
|
|
52
|
+
def _mro_collect_decorated_hooks_cached(
|
|
53
|
+
table: type, visible_aliases_fs: frozenset[str]
|
|
54
|
+
) -> Dict[str, Dict[str, list[Callable[..., Any]]]]:
|
|
55
|
+
"""Cached helper for :func:`mro_collect_decorated_hooks`."""
|
|
56
|
+
|
|
57
|
+
visible_aliases = set(visible_aliases_fs)
|
|
58
|
+
logger.info("Collecting hooks for %s", table.__name__)
|
|
59
|
+
mapping: Dict[str, Dict[str, list[Callable[..., Any]]]] = {}
|
|
60
|
+
aliases = mro_alias_map_for(table)
|
|
61
|
+
|
|
62
|
+
def _resolve_ops(spec: Union[str, Iterable[str]]) -> Iterable[str]:
|
|
63
|
+
if spec == "*":
|
|
64
|
+
return visible_aliases
|
|
65
|
+
if isinstance(spec, str):
|
|
66
|
+
return [spec if spec in visible_aliases else apply_alias(spec, aliases)]
|
|
67
|
+
out: list[str] = []
|
|
68
|
+
for x in spec:
|
|
69
|
+
out.append(x if x in visible_aliases else apply_alias(x, aliases))
|
|
70
|
+
return out
|
|
71
|
+
|
|
72
|
+
for base in reversed(table.__mro__):
|
|
73
|
+
for name, attr in base.__dict__.items():
|
|
74
|
+
func = _unwrap(attr)
|
|
75
|
+
decls: list[Hook] | None = getattr(func, HOOK_DECLS_ATTR, None)
|
|
76
|
+
if not decls:
|
|
77
|
+
continue
|
|
78
|
+
for d in decls:
|
|
79
|
+
for op in _resolve_ops(d.ops):
|
|
80
|
+
if op not in visible_aliases:
|
|
81
|
+
continue
|
|
82
|
+
ph = d.phase
|
|
83
|
+
mapping.setdefault(op, {}).setdefault(ph, []).append(
|
|
84
|
+
_wrap_ctx_hook(table, d.fn, ph)
|
|
85
|
+
)
|
|
86
|
+
logger.debug("Collected hooks for aliases: %s", list(mapping.keys()))
|
|
87
|
+
return mapping
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def mro_collect_decorated_hooks(
|
|
91
|
+
table: type, *, visible_aliases: set[str]
|
|
92
|
+
) -> Dict[str, Dict[str, list[Callable[..., Any]]]]:
|
|
93
|
+
"""Collect alias→phase→[hook] declarations across a table's MRO."""
|
|
94
|
+
|
|
95
|
+
return _mro_collect_decorated_hooks_cached(table, frozenset(visible_aliases))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
__all__ = ["mro_collect_decorated_hooks"]
|
tigrbl/hook/shortcuts.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Shortcut helpers for building Hook specs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable, Union
|
|
6
|
+
|
|
7
|
+
from .types import HookPhase, HookPredicate, StepFn
|
|
8
|
+
from ._hook import Hook
|
|
9
|
+
from .hook_spec import HookSpec
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def hook(
|
|
13
|
+
phase: HookPhase,
|
|
14
|
+
ops: Union[str, Iterable[str]],
|
|
15
|
+
fn: StepFn,
|
|
16
|
+
*,
|
|
17
|
+
name: str | None = None,
|
|
18
|
+
description: str | None = None,
|
|
19
|
+
) -> Hook:
|
|
20
|
+
"""Build a :class:`Hook` instance."""
|
|
21
|
+
return Hook(phase=phase, fn=fn, ops=ops, name=name, description=description)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def hook_spec(
|
|
25
|
+
phase: HookPhase,
|
|
26
|
+
fn: StepFn,
|
|
27
|
+
*,
|
|
28
|
+
order: int = 0,
|
|
29
|
+
when: HookPredicate | None = None,
|
|
30
|
+
name: str | None = None,
|
|
31
|
+
description: str | None = None,
|
|
32
|
+
) -> HookSpec:
|
|
33
|
+
"""Build a :class:`HookSpec` instance."""
|
|
34
|
+
return HookSpec(
|
|
35
|
+
phase=phase,
|
|
36
|
+
fn=fn,
|
|
37
|
+
order=order,
|
|
38
|
+
when=when,
|
|
39
|
+
name=name,
|
|
40
|
+
description=description,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ["hook", "hook_spec"]
|
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"]
|