tigrbl-base 0.1.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. tigrbl_base-0.1.0.dev5/PKG-INFO +53 -0
  2. tigrbl_base-0.1.0.dev5/README.md +29 -0
  3. tigrbl_base-0.1.0.dev5/pyproject.toml +48 -0
  4. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/__init__.py +40 -0
  5. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_alias_base.py +51 -0
  6. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_app_base.py +150 -0
  7. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_assembly.py +58 -0
  8. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_binding_base.py +14 -0
  9. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_column_base.py +101 -0
  10. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_engine_base.py +15 -0
  11. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_engine_provider_base.py +15 -0
  12. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_headers_base.py +12 -0
  13. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_hook_base.py +25 -0
  14. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_mapping_access.py +19 -0
  15. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_middleware_base.py +145 -0
  16. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_op_base.py +12 -0
  17. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_request_base.py +53 -0
  18. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_response_base.py +103 -0
  19. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_rest_map.py +122 -0
  20. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_router_base.py +60 -0
  21. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_rpc_map.py +511 -0
  22. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_schema_base.py +52 -0
  23. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_security_base.py +69 -0
  24. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_session_abc.py +47 -0
  25. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_session_base.py +132 -0
  26. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_storage.py +18 -0
  27. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_table_base.py +539 -0
  28. tigrbl_base-0.1.0.dev5/tigrbl_base/_base/_table_registry_base.py +55 -0
  29. tigrbl_base-0.1.0.dev5/tigrbl_base/column/__init__.py +123 -0
  30. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/__init__.py +25 -0
  31. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/core.py +91 -0
  32. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/jsonhints.py +44 -0
  33. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/planning.py +124 -0
  34. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/types.py +82 -0
  35. tigrbl_base-0.1.0.dev5/tigrbl_base/column/infer/utils.py +59 -0
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: tigrbl-base
3
+ Version: 0.1.0.dev5
4
+ Summary: Abstract base interfaces for Tigrbl APIs and runtime components.
5
+ License-Expression: Apache-2.0
6
+ Keywords: tigrbl,sdk,standards,framework
7
+ Author: Jacob Stewart
8
+ Author-email: jacob@swarmauri.com
9
+ Requires-Python: >=3.10,<3.13
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Development Status :: 1 - Planning
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Requires-Dist: pydantic (>=2.0)
19
+ Requires-Dist: sqlalchemy (>=2.0)
20
+ Requires-Dist: tigrbl-atoms
21
+ Requires-Dist: tigrbl-core
22
+ Description-Content-Type: text/markdown
23
+
24
+ ![Tigrbl branding](https://github.com/swarmauri/swarmauri-sdk/blob/a170683ecda8ca1c4f912c966d4499649ffb8224/assets/tigrbl.brand.theme.svg)
25
+
26
+ # tigrbl-base
27
+
28
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/tigrbl-base.svg) ![Hits](https://hits.sh/github.com/swarmauri/swarmauri-sdk.svg) ![Python Versions](https://img.shields.io/pypi/pyversions/tigrbl-base.svg) ![License](https://img.shields.io/pypi/l/tigrbl-base.svg) ![Version](https://img.shields.io/pypi/v/tigrbl-base.svg)
29
+
30
+ ## Features
31
+
32
+ - Modular package in the Tigrbl namespace.
33
+ - Supports Python 3.10 through 3.12.
34
+ - Distributed as part of the swarmauri-sdk workspace.
35
+
36
+ ## Installation
37
+
38
+ ### uv
39
+
40
+ ```bash
41
+ uv add tigrbl-base
42
+ ```
43
+
44
+ ### pip
45
+
46
+ ```bash
47
+ pip install tigrbl-base
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ Import from the shared package-specific module namespaces after installation in your environment.
53
+
@@ -0,0 +1,29 @@
1
+ ![Tigrbl branding](https://github.com/swarmauri/swarmauri-sdk/blob/a170683ecda8ca1c4f912c966d4499649ffb8224/assets/tigrbl.brand.theme.svg)
2
+
3
+ # tigrbl-base
4
+
5
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/tigrbl-base.svg) ![Hits](https://hits.sh/github.com/swarmauri/swarmauri-sdk.svg) ![Python Versions](https://img.shields.io/pypi/pyversions/tigrbl-base.svg) ![License](https://img.shields.io/pypi/l/tigrbl-base.svg) ![Version](https://img.shields.io/pypi/v/tigrbl-base.svg)
6
+
7
+ ## Features
8
+
9
+ - Modular package in the Tigrbl namespace.
10
+ - Supports Python 3.10 through 3.12.
11
+ - Distributed as part of the swarmauri-sdk workspace.
12
+
13
+ ## Installation
14
+
15
+ ### uv
16
+
17
+ ```bash
18
+ uv add tigrbl-base
19
+ ```
20
+
21
+ ### pip
22
+
23
+ ```bash
24
+ pip install tigrbl-base
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Import from the shared package-specific module namespaces after installation in your environment.
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "tigrbl-base"
3
+ version = "0.1.0.dev5"
4
+ description = "Abstract base interfaces for Tigrbl APIs and runtime components."
5
+ license = "Apache-2.0"
6
+ readme = "README.md"
7
+ repository = "http://github.com/swarmauri/swarmauri-sdk"
8
+ requires-python = ">=3.10,<3.13"
9
+ classifiers = [
10
+ "License :: OSI Approved :: Apache Software License",
11
+ "Development Status :: 1 - Planning",
12
+ "Programming Language :: Python :: 3.10",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ ]
19
+ authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
20
+ dependencies = [
21
+ "tigrbl-core",
22
+ "tigrbl-atoms",
23
+ "sqlalchemy>=2.0",
24
+ "pydantic>=2.0",
25
+ ]
26
+ keywords = ["tigrbl", "sdk", "standards", "framework"]
27
+
28
+ [tool.uv.sources]
29
+ "tigrbl-core" = { workspace = true }
30
+ "tigrbl-atoms" = { workspace = true }
31
+
32
+ [build-system]
33
+ requires = ["poetry-core>=1.0.0"]
34
+ build-backend = "poetry.core.masonry.api"
35
+
36
+
37
+ [tool.poetry]
38
+ packages = [
39
+ { include = "tigrbl_base" },
40
+ ]
41
+
42
+ [dependency-groups]
43
+ dev = [
44
+ "pytest>=8.0",
45
+ "pytest-asyncio>=0.24.0",
46
+ "pytest-timeout>=2.3.1",
47
+ "ruff>=0.9",
48
+ ]
@@ -0,0 +1,40 @@
1
+ """Base class implementations for tigrbl internals."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from typing import Any
7
+
8
+ _EXPORTS = {
9
+ "AppBase": "_app_base",
10
+ "BindingBase": "_binding_base",
11
+ "BindingRegistryBase": "_binding_base",
12
+ "ColumnBase": "_column_base",
13
+ "AliasBase": "_alias_base",
14
+ "EngineBase": "_engine_base",
15
+ "EngineProviderBase": "_engine_provider_base",
16
+ "HookBase": "_hook_base",
17
+ "RouterBase": "_router_base",
18
+ "ForeignKeyBase": "_storage",
19
+ "StorageTransformBase": "_storage",
20
+ "OpBase": "_op_base",
21
+ "RequestBase": "_request_base",
22
+ "SchemaBase": "_schema_base",
23
+ "SessionABC": "_session_abc",
24
+ "TigrblSessionBase": "_session_base",
25
+ "TableBase": "_table_base",
26
+ "TableRegistryBase": "_table_registry_base",
27
+ "AttrDict": "_mapping_access",
28
+ }
29
+
30
+ __all__ = list(_EXPORTS)
31
+
32
+
33
+ def __getattr__(name: str) -> Any:
34
+ module_name = _EXPORTS.get(name)
35
+ if module_name is None:
36
+ raise AttributeError(name)
37
+ module = import_module(f"{__name__}.{module_name}")
38
+ value = getattr(module, name)
39
+ globals()[name] = value
40
+ return value
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Any, Optional
5
+
6
+ from tigrbl_core._spec.alias_spec import AliasSpec
7
+ from tigrbl_core._spec.op_spec import Arity, PersistPolicy
8
+
9
+ if TYPE_CHECKING: # pragma: no cover
10
+ from tigrbl_core._spec.schema_spec import SchemaArg
11
+ else:
12
+ SchemaArg = Any
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class AliasBase(AliasSpec):
17
+ """Base alias contract implementation shared by concrete aliases."""
18
+
19
+ _alias: str
20
+ _request_schema: Optional[SchemaArg] = None
21
+ _response_schema: Optional[SchemaArg] = None
22
+ _persist: Optional[PersistPolicy] = None
23
+ _arity: Optional[Arity] = None
24
+ _rest: Optional[bool] = None
25
+
26
+ @property
27
+ def alias(self) -> str:
28
+ return self._alias
29
+
30
+ @property
31
+ def request_schema(self) -> Optional[SchemaArg]:
32
+ return self._request_schema
33
+
34
+ @property
35
+ def response_schema(self) -> Optional[SchemaArg]:
36
+ return self._response_schema
37
+
38
+ @property
39
+ def persist(self) -> Optional[PersistPolicy]:
40
+ return self._persist
41
+
42
+ @property
43
+ def arity(self) -> Optional[Arity]:
44
+ return self._arity
45
+
46
+ @property
47
+ def rest(self) -> Optional[bool]:
48
+ return self._rest
49
+
50
+
51
+ __all__ = ["AliasBase"]
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import inspect
5
+ from typing import Any
6
+ from typing import Callable, Optional, Sequence
7
+
8
+ from tigrbl_core._spec.app_spec import AppSpec
9
+ from tigrbl_core._spec.engine_spec import EngineCfg
10
+ from tigrbl_core._spec.response_spec import ResponseSpec
11
+
12
+
13
+ @dataclass(eq=False)
14
+ class AppBase(AppSpec):
15
+ """Base app configuration helpers shared by concrete app implementations."""
16
+
17
+ title: str = "Tigrbl"
18
+ description: str | None = None
19
+ version: str = "0.1.0"
20
+ engine: Optional[EngineCfg] = None
21
+ routers: Sequence[Any] = ()
22
+ ops: Sequence[Any] = ()
23
+ tables: Sequence[Any] = ()
24
+ schemas: Sequence[Any] = ()
25
+ hooks: Sequence[Callable[..., Any]] = ()
26
+ security_deps: Sequence[Callable[..., Any]] = ()
27
+ deps: Sequence[Callable[..., Any]] = ()
28
+ response: Optional[ResponseSpec] = None
29
+ jsonrpc_prefix: str = "/rpc"
30
+ system_prefix: str = "/system"
31
+ middlewares: Sequence[Any] = ()
32
+ lifespan: Optional[Callable[..., Any]] = None
33
+
34
+ @staticmethod
35
+ def _is_bindable_child_class(candidate: Any) -> bool:
36
+ if not inspect.isclass(candidate):
37
+ return False
38
+
39
+ bindable_types: list[type[Any]] = []
40
+ for mod_name, type_name in (
41
+ ("._table_base", "TableBase"),
42
+ ("._op_base", "OpBase"),
43
+ ("._hook_base", "HookBase"),
44
+ ("._schema_base", "SchemaBase"),
45
+ ):
46
+ try:
47
+ module = __import__(
48
+ f"tigrbl_base._base{mod_name}", fromlist=[type_name]
49
+ )
50
+ bindable = getattr(module, type_name, None)
51
+ if isinstance(bindable, type):
52
+ bindable_types.append(bindable)
53
+ except Exception:
54
+ continue
55
+
56
+ if not bindable_types:
57
+ return False
58
+
59
+ try:
60
+ return issubclass(candidate, tuple(bindable_types))
61
+ except TypeError:
62
+ return False
63
+
64
+ @classmethod
65
+ def _bind_child_to_parent(cls, child: Any, *, parent: object | None) -> Any:
66
+ if parent is None:
67
+ return child
68
+
69
+ target = child
70
+ if cls._is_bindable_child_class(child):
71
+ try:
72
+ target = child()
73
+ except Exception:
74
+ target = child
75
+
76
+ for attr in ("app", "parent", "owner"):
77
+ if hasattr(target, attr):
78
+ try:
79
+ setattr(target, attr, parent)
80
+ except Exception:
81
+ pass
82
+
83
+ for attr in ("APP", "PARENT", "OWNER"):
84
+ if hasattr(target, attr):
85
+ try:
86
+ setattr(target, attr, parent)
87
+ except Exception:
88
+ pass
89
+
90
+ return target
91
+
92
+ @classmethod
93
+ def collect_spec(cls, app: type) -> AppSpec:
94
+ """Collect and normalize an ``AppSpec`` snapshot from an app type."""
95
+
96
+ spec = AppSpec.collect(app)
97
+ routers = tuple(dict.fromkeys(tuple(spec.routers or ())))
98
+ return AppSpec(
99
+ title=spec.title,
100
+ description=spec.description,
101
+ version=spec.version,
102
+ engine=spec.engine,
103
+ routers=routers,
104
+ ops=tuple(spec.ops or ()),
105
+ tables=tuple(spec.tables or ()),
106
+ schemas=tuple(spec.schemas or ()),
107
+ hooks=tuple(spec.hooks or ()),
108
+ security_deps=tuple(spec.security_deps or ()),
109
+ deps=tuple(spec.deps or ()),
110
+ response=spec.response,
111
+ jsonrpc_prefix=spec.jsonrpc_prefix,
112
+ system_prefix=spec.system_prefix,
113
+ middlewares=tuple(spec.middlewares or ()),
114
+ lifespan=spec.lifespan,
115
+ )
116
+
117
+ @classmethod
118
+ def _bind_mapped_children(
119
+ cls, children: Sequence[Any] | None, *, parent: object | None
120
+ ) -> tuple[Any, ...]:
121
+ return tuple(
122
+ cls._bind_child_to_parent(child, parent=parent)
123
+ for child in tuple(children or ())
124
+ )
125
+
126
+ @classmethod
127
+ def bind_spec(cls, spec: AppSpec, *, parent: object | None = None) -> AppSpec:
128
+ """Bind a collected AppSpec by attaching mapped children to the parent."""
129
+
130
+ return AppSpec(
131
+ title=str(spec.title or "Tigrbl"),
132
+ description=spec.description,
133
+ version=str(spec.version or "0.1.0"),
134
+ engine=spec.engine,
135
+ routers=cls._bind_mapped_children(spec.routers, parent=parent),
136
+ ops=cls._bind_mapped_children(spec.ops, parent=parent),
137
+ tables=cls._bind_mapped_children(spec.tables, parent=parent),
138
+ schemas=cls._bind_mapped_children(spec.schemas, parent=parent),
139
+ hooks=cls._bind_mapped_children(spec.hooks, parent=parent),
140
+ security_deps=tuple(spec.security_deps or ()),
141
+ deps=tuple(spec.deps or ()),
142
+ response=spec.response,
143
+ jsonrpc_prefix=str(spec.jsonrpc_prefix or "/rpc"),
144
+ system_prefix=str(spec.system_prefix or "/system"),
145
+ middlewares=tuple(spec.middlewares or ()),
146
+ lifespan=spec.lifespan,
147
+ )
148
+
149
+
150
+ __all__ = ["AppBase"]
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import singledispatch
4
+ from typing import Any, Iterable
5
+
6
+
7
+ _CHILD_ATTRS = ("routers", "tables", "ops", "hooks", "schemas", "columns")
8
+
9
+
10
+ def iter_children(spec: Any) -> Iterable[tuple[str, str, Any]]:
11
+ for namespace in _CHILD_ATTRS:
12
+ children = getattr(spec, namespace, ()) or ()
13
+ if isinstance(children, dict):
14
+ for key, child in children.items():
15
+ yield namespace, str(key), child
16
+ continue
17
+ for child in tuple(children):
18
+ key = str(
19
+ getattr(child, "name", None)
20
+ or getattr(child, "alias", None)
21
+ or id(child)
22
+ )
23
+ yield namespace, key, child
24
+
25
+
26
+ def bind_child(parent: Any, namespace: str, key: str, child: Any) -> Any:
27
+ slot = getattr(parent, namespace, None)
28
+ if hasattr(slot, "__setitem__"):
29
+ try:
30
+ slot[key] = child
31
+ return child
32
+ except Exception:
33
+ pass
34
+ for attr in ("app", "parent", "owner"):
35
+ if hasattr(child, attr):
36
+ try:
37
+ setattr(child, attr, parent)
38
+ except Exception:
39
+ pass
40
+ return child
41
+
42
+
43
+ @singledispatch
44
+ def maybe_convert(parent: Any, namespace: str, key: str, child: Any) -> Any:
45
+ return child
46
+
47
+
48
+ def assemble(owner: Any) -> Any:
49
+ collector = getattr(type(owner), "collect", None)
50
+ spec = collector(owner) if callable(collector) else owner
51
+ for namespace, key, child in iter_children(spec):
52
+ assembled_child = assemble(child)
53
+ converted = maybe_convert(owner, namespace, key, assembled_child)
54
+ bind_child(owner, namespace, key, converted)
55
+ finalize = getattr(owner, "finalize", None)
56
+ if callable(finalize):
57
+ finalize(spec)
58
+ return owner
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from tigrbl_core._spec.binding_spec import BindingRegistrySpec, BindingSpec
4
+
5
+
6
+ class BindingBase(BindingSpec):
7
+ """Base named binding declaration."""
8
+
9
+
10
+ class BindingRegistryBase(BindingRegistrySpec):
11
+ """Base named binding registry."""
12
+
13
+
14
+ __all__ = ["BindingBase", "BindingRegistryBase"]
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, Optional
4
+
5
+ from sqlalchemy import ForeignKey
6
+ from sqlalchemy.orm import MappedColumn
7
+
8
+ from tigrbl_core._spec.column_spec import ColumnSpec
9
+ from tigrbl_core._spec.field_spec import FieldSpec as F
10
+ from tigrbl_core._spec.io_spec import IOSpec as IO
11
+ from tigrbl_core._spec.storage_spec import StorageSpec as S
12
+
13
+
14
+ class ColumnBase(ColumnSpec, MappedColumn):
15
+ """Base SQLAlchemy column descriptor implementing :class:`ColumnSpec`."""
16
+
17
+ __slots__ = ()
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ spec: ColumnSpec | None = None,
23
+ storage: S | None = None,
24
+ field: F | None = None,
25
+ io: IO | None = None,
26
+ default_factory: Optional[Callable[[dict], Any]] = None,
27
+ read_producer: Optional[Callable[[object, dict], Any]] = None,
28
+ **kw: Any,
29
+ ) -> None:
30
+ if spec is not None and any(
31
+ x is not None for x in (storage, field, io, default_factory, read_producer)
32
+ ):
33
+ raise ValueError("Provide either spec or individual components, not both.")
34
+
35
+ if spec is None:
36
+ spec = ColumnSpec(
37
+ storage=storage,
38
+ field=field,
39
+ io=io,
40
+ default_factory=default_factory,
41
+ read_producer=read_producer,
42
+ )
43
+ else:
44
+ storage = spec.storage
45
+ field = spec.field
46
+ io = spec.io
47
+ default_factory = spec.default_factory
48
+ read_producer = spec.read_producer
49
+
50
+ s = storage
51
+ if s is not None:
52
+ args: list[Any] = [s.type_]
53
+ fk = getattr(s, "fk", None)
54
+ if fk is not None:
55
+ args.append(
56
+ ForeignKey(
57
+ fk.target,
58
+ ondelete=fk.on_delete,
59
+ onupdate=fk.on_update,
60
+ deferrable=fk.deferrable,
61
+ initially="DEFERRED" if fk.initially_deferred else "IMMEDIATE",
62
+ match=fk.match,
63
+ )
64
+ )
65
+ MappedColumn.__init__(
66
+ self,
67
+ *args,
68
+ primary_key=s.primary_key,
69
+ nullable=s.nullable,
70
+ unique=s.unique,
71
+ index=s.index,
72
+ default=s.default,
73
+ autoincrement=s.autoincrement,
74
+ server_default=s.server_default,
75
+ onupdate=s.onupdate,
76
+ comment=s.comment,
77
+ **kw,
78
+ )
79
+ else:
80
+ MappedColumn.__init__(self, **kw)
81
+
82
+ self.storage = s
83
+ self.field = field if field is not None else F()
84
+ self.io = io if io is not None else IO()
85
+ self.default_factory = default_factory
86
+ self.read_producer = read_producer
87
+
88
+ def __set_name__(self, owner: type, name: str) -> None:
89
+ parent = getattr(super(), "__set_name__", None)
90
+ if parent:
91
+ parent(owner, name)
92
+
93
+ colspecs = owner.__dict__.get("__tigrbl_colspecs__")
94
+ if colspecs is None:
95
+ base_specs = getattr(owner, "__tigrbl_colspecs__", {})
96
+ colspecs = dict(base_specs)
97
+ setattr(owner, "__tigrbl_colspecs__", colspecs)
98
+ colspecs[name] = self
99
+
100
+
101
+ __all__ = ["ColumnBase"]
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class EngineBase:
7
+ """Base contract for concrete engine façade implementations."""
8
+
9
+ spec: Any
10
+
11
+ def to_provider(self) -> Any: # pragma: no cover - interface contract
12
+ raise NotImplementedError
13
+
14
+
15
+ __all__ = ["EngineBase"]
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Protocol, runtime_checkable
4
+
5
+
6
+ @runtime_checkable
7
+ class EngineProviderBase(Protocol):
8
+ """Provider-like runtime contract for engine provider objects."""
9
+
10
+ spec: Any
11
+
12
+ def to_provider(self) -> "EngineProviderBase": ...
13
+
14
+
15
+ __all__ = ["EngineProviderBase"]
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class HeadersBase(dict[str, str]):
5
+ """Base header mapping type for response implementations."""
6
+
7
+
8
+ class HeaderCookiesBase(dict[str, str]):
9
+ """Base cookie mapping type for response implementations."""
10
+
11
+
12
+ __all__ = ["HeaderCookiesBase", "HeadersBase"]
@@ -0,0 +1,25 @@
1
+ """Base 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 tigrbl_core._spec.hook_spec import HookSpec
9
+ from tigrbl_atoms import HookPhase, StepFn
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class HookBase(HookSpec):
14
+ """Base hook bound to a phase and one or more ops."""
15
+
16
+ phase: HookPhase
17
+ fn: StepFn
18
+ ops: Union[str, Iterable[str]] = "*"
19
+ order: int = 0
20
+ when: Optional[object] = None
21
+ name: Optional[str] = None
22
+ description: Optional[str] = None
23
+
24
+
25
+ __all__ = ["HookBase"]
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class AttrDict(dict):
7
+ """Dictionary that also supports attribute-style access."""
8
+
9
+ def __getattr__(self, item: str) -> Any: # pragma: no cover - trivial
10
+ try:
11
+ return self[item]
12
+ except KeyError as e: # pragma: no cover - debug helper
13
+ raise AttributeError(item) from e
14
+
15
+ def __setattr__(self, key: str, value: Any) -> None: # pragma: no cover - trivial
16
+ self[key] = value
17
+
18
+
19
+ __all__ = ["AttrDict"]