tigrbl-core 0.1.0__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.
- tigrbl_core-0.1.0/PKG-INFO +54 -0
- tigrbl_core-0.1.0/README.md +29 -0
- tigrbl_core-0.1.0/pyproject.toml +46 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/__init__.py +88 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/alias_spec.py +35 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/app_spec.py +216 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/binding_spec.py +78 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/column_spec.py +183 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/engine_spec.py +384 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/field_spec.py +49 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/hook_spec.py +42 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/hook_types.py +34 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/io_spec.py +137 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/middleware_spec.py +28 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/op_spec.py +452 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/op_utils.py +43 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/plugins.py +44 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/registry.py +38 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/request_spec.py +21 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/response_resolver.py +63 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/response_spec.py +36 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/response_types.py +12 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/router_spec.py +102 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/schema_spec.py +47 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/serde.py +164 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/session_spec.py +148 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/storage_spec.py +79 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/table_registry_spec.py +16 -0
- tigrbl_core-0.1.0/tigrbl_core/_spec/table_spec.py +118 -0
- tigrbl_core-0.1.0/tigrbl_core/config/__init__.py +6 -0
- tigrbl_core-0.1.0/tigrbl_core/config/constants.py +224 -0
- tigrbl_core-0.1.0/tigrbl_core/config/defaults.py +32 -0
- tigrbl_core-0.1.0/tigrbl_core/config/engine_traversal.py +102 -0
- tigrbl_core-0.1.0/tigrbl_core/config/resolver.py +276 -0
- tigrbl_core-0.1.0/tigrbl_core/core/__init__.py +7 -0
- tigrbl_core-0.1.0/tigrbl_core/op/__init__.py +17 -0
- tigrbl_core-0.1.0/tigrbl_core/op/canonical.py +39 -0
- tigrbl_core-0.1.0/tigrbl_core/op/collect.py +20 -0
- tigrbl_core-0.1.0/tigrbl_core/op/types.py +29 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/__init__.py +28 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/__init__.py +17 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/build_schema.py +307 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/cache.py +24 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/extras.py +85 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/helpers.py +87 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/list_params.py +117 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/builder/strip_parent_fields.py +70 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/get_schema.py +85 -0
- tigrbl_core-0.1.0/tigrbl_core/schema/utils.py +142 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tigrbl-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Core specifications and primitives for the Tigrbl framework.
|
|
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.10,<3)
|
|
19
|
+
Requires-Dist: pyyaml
|
|
20
|
+
Requires-Dist: tigrbl-typing
|
|
21
|
+
Requires-Dist: tomli (>=2.0.1) ; python_version < "3.11"
|
|
22
|
+
Requires-Dist: tomli-w
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
# tigrbl-core
|
|
28
|
+
|
|
29
|
+
    
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- Core specification and primitive contracts for Tigrbl.
|
|
34
|
+
- Operational implementations now live in `tigrbl-ops-oltp`.
|
|
35
|
+
- Supports Python 3.10 through 3.12.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### uv
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv add tigrbl-core
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### pip
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install tigrbl-core
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
Import from the shared package-specific module namespaces after installation in your environment.
|
|
54
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# tigrbl-core
|
|
4
|
+
|
|
5
|
+
    
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Core specification and primitive contracts for Tigrbl.
|
|
10
|
+
- Operational implementations now live in `tigrbl-ops-oltp`.
|
|
11
|
+
- Supports Python 3.10 through 3.12.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### uv
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add tigrbl-core
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### pip
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install tigrbl-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Import from the shared package-specific module namespaces after installation in your environment.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tigrbl-core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Core specifications and primitives for the Tigrbl framework."
|
|
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-typing",
|
|
22
|
+
"pydantic>=2.10,<3",
|
|
23
|
+
"pyyaml",
|
|
24
|
+
"tomli-w",
|
|
25
|
+
"tomli>=2.0.1; python_version < '3.11'",
|
|
26
|
+
]
|
|
27
|
+
keywords = ["tigrbl", "sdk", "standards", "framework"]
|
|
28
|
+
|
|
29
|
+
[tool.uv.sources]
|
|
30
|
+
"tigrbl-typing" = { 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_core" },
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8.0",
|
|
45
|
+
"ruff>=0.9",
|
|
46
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Canonical spec package.
|
|
2
|
+
|
|
3
|
+
Keep this module import-light to avoid circular imports during package startup.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from importlib import import_module
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
_EXPORTS = {
|
|
12
|
+
"AliasSpec": "alias_spec",
|
|
13
|
+
"AppSpec": "app_spec",
|
|
14
|
+
"BindingSpec": "binding_spec",
|
|
15
|
+
"BindingRegistrySpec": "binding_spec",
|
|
16
|
+
"TransportBindingSpec": "binding_spec",
|
|
17
|
+
"HttpRestBindingSpec": "binding_spec",
|
|
18
|
+
"HttpJsonRpcBindingSpec": "binding_spec",
|
|
19
|
+
"WsBindingSpec": "binding_spec",
|
|
20
|
+
"resolve_rest_nested_prefix": "binding_spec",
|
|
21
|
+
"ColumnSpec": "column_spec",
|
|
22
|
+
"EngineSpec": "engine_spec",
|
|
23
|
+
"EngineProviderSpec": "engine_spec",
|
|
24
|
+
"FieldSpec": "field_spec",
|
|
25
|
+
"F": "field_spec",
|
|
26
|
+
"HookSpec": "hook_spec",
|
|
27
|
+
"IOSpec": "io_spec",
|
|
28
|
+
"IO": "io_spec",
|
|
29
|
+
"MiddlewareSpec": "middleware_spec",
|
|
30
|
+
"OpSpec": "op_spec",
|
|
31
|
+
"Arity": "op_spec",
|
|
32
|
+
"TargetOp": "op_spec",
|
|
33
|
+
"PersistPolicy": "op_spec",
|
|
34
|
+
"PHASE": "op_spec",
|
|
35
|
+
"PHASES": "op_spec",
|
|
36
|
+
"HookPhase": "hook_spec",
|
|
37
|
+
"TemplateSpec": "response_spec",
|
|
38
|
+
"ResponseSpec": "response_spec",
|
|
39
|
+
"resolve_response_spec": "response_resolver",
|
|
40
|
+
"RequestSpec": "request_spec",
|
|
41
|
+
"RouterSpec": "router_spec",
|
|
42
|
+
"SchemaSpec": "schema_spec",
|
|
43
|
+
"SchemaRef": "schema_spec",
|
|
44
|
+
"SchemaArg": "schema_spec",
|
|
45
|
+
"SessionSpec": "session_spec",
|
|
46
|
+
"session_spec": "session_spec",
|
|
47
|
+
"tx_read_committed": "session_spec",
|
|
48
|
+
"tx_repeatable_read": "session_spec",
|
|
49
|
+
"tx_serializable": "session_spec",
|
|
50
|
+
"readonly": "session_spec",
|
|
51
|
+
"StorageSpec": "storage_spec",
|
|
52
|
+
"S": "storage_spec",
|
|
53
|
+
"StorageTransformSpec": "storage_spec",
|
|
54
|
+
"StorageTransform": "storage_spec",
|
|
55
|
+
"ForeignKeySpec": "storage_spec",
|
|
56
|
+
"TableSpec": "table_spec",
|
|
57
|
+
"TableRegistrySpec": "table_registry_spec",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
__all__ = list(_EXPORTS)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def __getattr__(name: str) -> Any:
|
|
64
|
+
if name in {"PHASE", "PHASES"}:
|
|
65
|
+
from .hook_types import HookPhase, HookPhases
|
|
66
|
+
|
|
67
|
+
value = HookPhase if name == "PHASE" else HookPhases
|
|
68
|
+
globals()[name] = value
|
|
69
|
+
return value
|
|
70
|
+
if name in {"F", "IO", "S"}:
|
|
71
|
+
aliases = {
|
|
72
|
+
"F": ("field_spec", "FieldSpec"),
|
|
73
|
+
"IO": ("io_spec", "IOSpec"),
|
|
74
|
+
"S": ("storage_spec", "StorageSpec"),
|
|
75
|
+
}
|
|
76
|
+
module_name, attr = aliases[name]
|
|
77
|
+
module = import_module(f"{__name__}.{module_name}")
|
|
78
|
+
value = getattr(module, attr)
|
|
79
|
+
globals()[name] = value
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
module_name = _EXPORTS.get(name)
|
|
83
|
+
if module_name is None:
|
|
84
|
+
raise AttributeError(name)
|
|
85
|
+
module = import_module(f"{__name__}.{module_name}")
|
|
86
|
+
value = getattr(module, name)
|
|
87
|
+
globals()[name] = value
|
|
88
|
+
return value
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from .._spec.op_spec import Arity, PersistPolicy
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
9
|
+
from .schema_spec import SchemaArg
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AliasSpec(ABC):
|
|
13
|
+
@property
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def alias(self) -> str: ...
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def request_schema(self) -> Optional[SchemaArg]: ...
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def response_schema(self) -> Optional[SchemaArg]: ...
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def persist(self) -> Optional[PersistPolicy]: ...
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def arity(self) -> Optional[Arity]: ...
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def rest(self) -> Optional[bool]: ...
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# pkgs/standards/tigrbl_core/tigrbl/_spec/app_spec.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Callable, Iterable, Mapping, Optional, Sequence
|
|
5
|
+
|
|
6
|
+
from .._spec.engine_spec import EngineCfg
|
|
7
|
+
from .._spec.response_spec import ResponseSpec
|
|
8
|
+
from .serde import SerdeMixin
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _seqify(value: Any) -> tuple[Any, ...]:
|
|
12
|
+
"""Normalize sequence-like inputs while treating scalars as a single item."""
|
|
13
|
+
|
|
14
|
+
if value is None:
|
|
15
|
+
return ()
|
|
16
|
+
if isinstance(value, tuple):
|
|
17
|
+
return value
|
|
18
|
+
if isinstance(value, (str, bytes, bytearray)):
|
|
19
|
+
return (value,)
|
|
20
|
+
if isinstance(value, Mapping):
|
|
21
|
+
return (value,)
|
|
22
|
+
if isinstance(value, Iterable):
|
|
23
|
+
return tuple(value)
|
|
24
|
+
return (value,)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def merge_seq_attr(
|
|
28
|
+
owner: type,
|
|
29
|
+
attr: str,
|
|
30
|
+
*,
|
|
31
|
+
include_inherited: bool = False,
|
|
32
|
+
reverse: bool = False,
|
|
33
|
+
dedupe: bool = True,
|
|
34
|
+
) -> tuple[Any, ...]:
|
|
35
|
+
"""Merge sequence-like class attributes over the MRO."""
|
|
36
|
+
|
|
37
|
+
values: list[Any] = []
|
|
38
|
+
seen_hashable: set[Any] = set()
|
|
39
|
+
mro = reversed(owner.__mro__) if reverse else owner.__mro__
|
|
40
|
+
for base in mro:
|
|
41
|
+
if include_inherited:
|
|
42
|
+
if not hasattr(base, attr):
|
|
43
|
+
continue
|
|
44
|
+
seq = getattr(base, attr) or ()
|
|
45
|
+
else:
|
|
46
|
+
seq = base.__dict__.get(attr, ()) or ()
|
|
47
|
+
for item in _seqify(seq):
|
|
48
|
+
if dedupe:
|
|
49
|
+
try:
|
|
50
|
+
if item in seen_hashable:
|
|
51
|
+
continue
|
|
52
|
+
seen_hashable.add(item)
|
|
53
|
+
except TypeError:
|
|
54
|
+
if any(item == existing for existing in values):
|
|
55
|
+
continue
|
|
56
|
+
values.append(item)
|
|
57
|
+
return tuple(values)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def normalize_app_spec(spec: "AppSpec") -> "AppSpec":
|
|
61
|
+
"""Return a normalized app spec snapshot with stable sequence fields."""
|
|
62
|
+
|
|
63
|
+
routers = _seqify(spec.routers)
|
|
64
|
+
tables = _seqify(spec.tables)
|
|
65
|
+
ops = _seqify(spec.ops)
|
|
66
|
+
|
|
67
|
+
return AppSpec(
|
|
68
|
+
title=str(spec.title or "Tigrbl"),
|
|
69
|
+
description=spec.description,
|
|
70
|
+
version=str(spec.version or "0.1.0"),
|
|
71
|
+
engine=spec.engine,
|
|
72
|
+
routers=routers,
|
|
73
|
+
ops=ops,
|
|
74
|
+
tables=tables,
|
|
75
|
+
schemas=_seqify(spec.schemas),
|
|
76
|
+
hooks=_seqify(spec.hooks),
|
|
77
|
+
security_deps=_seqify(spec.security_deps),
|
|
78
|
+
deps=_seqify(spec.deps),
|
|
79
|
+
middlewares=_seqify(spec.middlewares),
|
|
80
|
+
response=spec.response,
|
|
81
|
+
jsonrpc_prefix=str(spec.jsonrpc_prefix or "/rpc"),
|
|
82
|
+
system_prefix=str(spec.system_prefix or "/system"),
|
|
83
|
+
lifespan=spec.lifespan,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(eq=False)
|
|
88
|
+
class AppSpec(SerdeMixin):
|
|
89
|
+
"""
|
|
90
|
+
Used to *produce an App subclass* via App.from_spec().
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
title: str = "Tigrbl"
|
|
94
|
+
description: str | None = None
|
|
95
|
+
version: str = "0.1.0"
|
|
96
|
+
engine: Optional[EngineCfg] = None
|
|
97
|
+
|
|
98
|
+
# NEW: multi-Router composition (store Router classes or instances)
|
|
99
|
+
routers: Sequence[Any] = field(default_factory=tuple)
|
|
100
|
+
|
|
101
|
+
# NEW: orchestration/topology knobs
|
|
102
|
+
ops: Sequence[Any] = field(default_factory=tuple) # op descriptors or specs
|
|
103
|
+
tables: Sequence[Any] = field(default_factory=tuple) # table refs owned by app
|
|
104
|
+
schemas: Sequence[Any] = field(default_factory=tuple) # schema classes/defs
|
|
105
|
+
hooks: Sequence[Callable[..., Any]] = field(default_factory=tuple)
|
|
106
|
+
|
|
107
|
+
# security/dep stacks (ASGI dependencies or callables)
|
|
108
|
+
security_deps: Sequence[Callable[..., Any]] = field(default_factory=tuple)
|
|
109
|
+
deps: Sequence[Callable[..., Any]] = field(default_factory=tuple)
|
|
110
|
+
|
|
111
|
+
# response defaults
|
|
112
|
+
response: Optional[ResponseSpec] = None
|
|
113
|
+
|
|
114
|
+
# system routing prefixes (REST/JSON-RPC namespaces)
|
|
115
|
+
jsonrpc_prefix: str = "/rpc"
|
|
116
|
+
system_prefix: str = "/system"
|
|
117
|
+
|
|
118
|
+
# optional framework bits
|
|
119
|
+
middlewares: Sequence[Any] = field(default_factory=tuple)
|
|
120
|
+
lifespan: Optional[Callable[..., Any]] = None
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def from_dict(cls, payload: dict[str, Any]) -> "AppSpec":
|
|
124
|
+
return super().from_dict(payload)
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def collect(cls, app: type) -> "AppSpec":
|
|
128
|
+
sentinel = object()
|
|
129
|
+
title: Any = sentinel
|
|
130
|
+
version: Any = sentinel
|
|
131
|
+
engine: Any | None = sentinel # type: ignore[assignment]
|
|
132
|
+
response: Any = sentinel
|
|
133
|
+
jsonrpc_prefix: Any = sentinel
|
|
134
|
+
system_prefix: Any = sentinel
|
|
135
|
+
lifespan: Any = sentinel
|
|
136
|
+
|
|
137
|
+
for base in app.__mro__:
|
|
138
|
+
if "TITLE" in base.__dict__ and title is sentinel:
|
|
139
|
+
title = base.__dict__["TITLE"]
|
|
140
|
+
if "VERSION" in base.__dict__ and version is sentinel:
|
|
141
|
+
version = base.__dict__["VERSION"]
|
|
142
|
+
if "ENGINE" in base.__dict__ and engine is sentinel:
|
|
143
|
+
engine = base.__dict__["ENGINE"]
|
|
144
|
+
if "RESPONSE" in base.__dict__ and response is sentinel:
|
|
145
|
+
response = base.__dict__["RESPONSE"]
|
|
146
|
+
if "JSONRPC_PREFIX" in base.__dict__ and jsonrpc_prefix is sentinel:
|
|
147
|
+
jsonrpc_prefix = base.__dict__["JSONRPC_PREFIX"]
|
|
148
|
+
if "SYSTEM_PREFIX" in base.__dict__ and system_prefix is sentinel:
|
|
149
|
+
system_prefix = base.__dict__["SYSTEM_PREFIX"]
|
|
150
|
+
if "LIFESPAN" in base.__dict__ and lifespan is sentinel:
|
|
151
|
+
lifespan = base.__dict__["LIFESPAN"]
|
|
152
|
+
|
|
153
|
+
if title is sentinel:
|
|
154
|
+
title = "Tigrbl"
|
|
155
|
+
if version is sentinel:
|
|
156
|
+
version = "0.1.0"
|
|
157
|
+
if engine is sentinel:
|
|
158
|
+
engine = None
|
|
159
|
+
if response is sentinel:
|
|
160
|
+
response = None
|
|
161
|
+
if jsonrpc_prefix is sentinel:
|
|
162
|
+
jsonrpc_prefix = "/rpc"
|
|
163
|
+
if system_prefix is sentinel:
|
|
164
|
+
system_prefix = "/system"
|
|
165
|
+
if lifespan is sentinel:
|
|
166
|
+
lifespan = None
|
|
167
|
+
|
|
168
|
+
description = getattr(app, "DESCRIPTION", None)
|
|
169
|
+
include_inherited_routers = "ROUTERS" not in app.__dict__
|
|
170
|
+
spec = cls(
|
|
171
|
+
title=title,
|
|
172
|
+
description=description,
|
|
173
|
+
version=version,
|
|
174
|
+
engine=engine,
|
|
175
|
+
routers=tuple(
|
|
176
|
+
merge_seq_attr(
|
|
177
|
+
app,
|
|
178
|
+
"ROUTERS",
|
|
179
|
+
include_inherited=include_inherited_routers,
|
|
180
|
+
reverse=include_inherited_routers,
|
|
181
|
+
dedupe=False,
|
|
182
|
+
)
|
|
183
|
+
or ()
|
|
184
|
+
),
|
|
185
|
+
ops=tuple(merge_seq_attr(app, "OPS") or ()),
|
|
186
|
+
tables=tuple(merge_seq_attr(app, "TABLES") or ()),
|
|
187
|
+
schemas=tuple(merge_seq_attr(app, "SCHEMAS") or ()),
|
|
188
|
+
hooks=tuple(merge_seq_attr(app, "HOOKS") or ()),
|
|
189
|
+
security_deps=tuple(merge_seq_attr(app, "SECURITY_DEPS") or ()),
|
|
190
|
+
deps=tuple(merge_seq_attr(app, "DEPS") or ()),
|
|
191
|
+
response=response,
|
|
192
|
+
jsonrpc_prefix=(jsonrpc_prefix or "/rpc"),
|
|
193
|
+
system_prefix=(system_prefix or "/system"),
|
|
194
|
+
middlewares=tuple(merge_seq_attr(app, "MIDDLEWARES") or ()),
|
|
195
|
+
lifespan=lifespan,
|
|
196
|
+
)
|
|
197
|
+
return normalize_app_spec(
|
|
198
|
+
cls(
|
|
199
|
+
title=spec.title,
|
|
200
|
+
description=spec.description,
|
|
201
|
+
version=spec.version,
|
|
202
|
+
engine=spec.engine,
|
|
203
|
+
routers=tuple(spec.routers or ()),
|
|
204
|
+
ops=tuple(spec.ops or ()),
|
|
205
|
+
tables=tuple(spec.tables or ()),
|
|
206
|
+
schemas=tuple(spec.schemas or ()),
|
|
207
|
+
hooks=tuple(spec.hooks or ()),
|
|
208
|
+
security_deps=tuple(spec.security_deps or ()),
|
|
209
|
+
deps=tuple(spec.deps or ()),
|
|
210
|
+
response=spec.response,
|
|
211
|
+
jsonrpc_prefix=(spec.jsonrpc_prefix or "/rpc"),
|
|
212
|
+
system_prefix=(spec.system_prefix or "/system"),
|
|
213
|
+
middlewares=tuple(spec.middlewares or ()),
|
|
214
|
+
lifespan=spec.lifespan,
|
|
215
|
+
)
|
|
216
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Literal, Optional, Type, Union
|
|
5
|
+
|
|
6
|
+
from ..config.constants import TIGRBL_NESTED_PATHS_ATTR
|
|
7
|
+
from .serde import SerdeMixin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class HttpRestBindingSpec(SerdeMixin):
|
|
12
|
+
proto: Literal["http.rest", "https.rest"]
|
|
13
|
+
methods: tuple[str, ...]
|
|
14
|
+
path: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class HttpJsonRpcBindingSpec(SerdeMixin):
|
|
19
|
+
proto: Literal["http.jsonrpc", "https.jsonrpc"]
|
|
20
|
+
rpc_method: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class WsBindingSpec(SerdeMixin):
|
|
25
|
+
proto: Literal["ws", "wss"]
|
|
26
|
+
path: str
|
|
27
|
+
subprotocols: tuple[str, ...] = ()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
TransportBindingSpec = Union[
|
|
31
|
+
HttpRestBindingSpec,
|
|
32
|
+
HttpJsonRpcBindingSpec,
|
|
33
|
+
WsBindingSpec,
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class BindingSpec(SerdeMixin):
|
|
39
|
+
"""Named binding declaration used for registry composition."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
spec: TransportBindingSpec
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(slots=True)
|
|
46
|
+
class BindingRegistrySpec(SerdeMixin):
|
|
47
|
+
"""Simple in-memory registry for named transport bindings."""
|
|
48
|
+
|
|
49
|
+
_bindings: dict[str, BindingSpec] = field(default_factory=dict)
|
|
50
|
+
|
|
51
|
+
def register(self, binding: BindingSpec) -> None:
|
|
52
|
+
self._bindings[binding.name] = binding
|
|
53
|
+
|
|
54
|
+
def get(self, name: str) -> Optional[BindingSpec]:
|
|
55
|
+
return self._bindings.get(name)
|
|
56
|
+
|
|
57
|
+
def values(self) -> tuple[BindingSpec, ...]:
|
|
58
|
+
return tuple(self._bindings.values())
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def resolve_rest_nested_prefix(model: Type) -> Optional[str]:
|
|
62
|
+
"""Return the configured nested REST prefix for ``model`` if present."""
|
|
63
|
+
|
|
64
|
+
cb = getattr(model, TIGRBL_NESTED_PATHS_ATTR, None)
|
|
65
|
+
if callable(cb):
|
|
66
|
+
return cb()
|
|
67
|
+
return getattr(model, "_nested_path", None)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
"BindingSpec",
|
|
72
|
+
"BindingRegistrySpec",
|
|
73
|
+
"HttpJsonRpcBindingSpec",
|
|
74
|
+
"HttpRestBindingSpec",
|
|
75
|
+
"TransportBindingSpec",
|
|
76
|
+
"WsBindingSpec",
|
|
77
|
+
"resolve_rest_nested_prefix",
|
|
78
|
+
]
|