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.
Files changed (252) hide show
  1. tigrbl/README.md +94 -0
  2. tigrbl/__init__.py +139 -14
  3. tigrbl/api/__init__.py +6 -0
  4. tigrbl/api/_api.py +72 -0
  5. tigrbl/api/api_spec.py +30 -0
  6. tigrbl/api/mro_collect.py +43 -0
  7. tigrbl/api/shortcuts.py +56 -0
  8. tigrbl/api/tigrbl_api.py +286 -0
  9. tigrbl/app/__init__.py +0 -0
  10. tigrbl/app/_app.py +61 -0
  11. tigrbl/app/app_spec.py +42 -0
  12. tigrbl/app/mro_collect.py +67 -0
  13. tigrbl/app/shortcuts.py +65 -0
  14. tigrbl/app/tigrbl_app.py +314 -0
  15. tigrbl/bindings/__init__.py +73 -0
  16. tigrbl/bindings/api/__init__.py +12 -0
  17. tigrbl/bindings/api/common.py +109 -0
  18. tigrbl/bindings/api/include.py +256 -0
  19. tigrbl/bindings/api/resource_proxy.py +149 -0
  20. tigrbl/bindings/api/rpc.py +111 -0
  21. tigrbl/bindings/columns.py +49 -0
  22. tigrbl/bindings/handlers/__init__.py +11 -0
  23. tigrbl/bindings/handlers/builder.py +119 -0
  24. tigrbl/bindings/handlers/ctx.py +74 -0
  25. tigrbl/bindings/handlers/identifiers.py +228 -0
  26. tigrbl/bindings/handlers/namespaces.py +51 -0
  27. tigrbl/bindings/handlers/steps.py +276 -0
  28. tigrbl/bindings/hooks.py +311 -0
  29. tigrbl/bindings/model.py +194 -0
  30. tigrbl/bindings/model_helpers.py +139 -0
  31. tigrbl/bindings/model_registry.py +77 -0
  32. tigrbl/bindings/rest/__init__.py +7 -0
  33. tigrbl/bindings/rest/attach.py +34 -0
  34. tigrbl/bindings/rest/collection.py +265 -0
  35. tigrbl/bindings/rest/common.py +116 -0
  36. tigrbl/bindings/rest/fastapi.py +76 -0
  37. tigrbl/bindings/rest/helpers.py +119 -0
  38. tigrbl/bindings/rest/io.py +317 -0
  39. tigrbl/bindings/rest/member.py +367 -0
  40. tigrbl/bindings/rest/router.py +292 -0
  41. tigrbl/bindings/rest/routing.py +133 -0
  42. tigrbl/bindings/rpc.py +364 -0
  43. tigrbl/bindings/schemas/__init__.py +11 -0
  44. tigrbl/bindings/schemas/builder.py +348 -0
  45. tigrbl/bindings/schemas/defaults.py +260 -0
  46. tigrbl/bindings/schemas/utils.py +193 -0
  47. tigrbl/column/README.md +62 -0
  48. tigrbl/column/__init__.py +72 -0
  49. tigrbl/column/_column.py +96 -0
  50. tigrbl/column/column_spec.py +40 -0
  51. tigrbl/column/field_spec.py +31 -0
  52. tigrbl/column/infer/__init__.py +25 -0
  53. tigrbl/column/infer/core.py +92 -0
  54. tigrbl/column/infer/jsonhints.py +44 -0
  55. tigrbl/column/infer/planning.py +133 -0
  56. tigrbl/column/infer/types.py +102 -0
  57. tigrbl/column/infer/utils.py +59 -0
  58. tigrbl/column/io_spec.py +133 -0
  59. tigrbl/column/mro_collect.py +59 -0
  60. tigrbl/column/shortcuts.py +89 -0
  61. tigrbl/column/storage_spec.py +65 -0
  62. tigrbl/config/__init__.py +19 -0
  63. tigrbl/config/constants.py +224 -0
  64. tigrbl/config/defaults.py +29 -0
  65. tigrbl/config/resolver.py +295 -0
  66. tigrbl/core/__init__.py +47 -0
  67. tigrbl/core/crud/__init__.py +36 -0
  68. tigrbl/core/crud/bulk.py +168 -0
  69. tigrbl/core/crud/helpers/__init__.py +76 -0
  70. tigrbl/core/crud/helpers/db.py +92 -0
  71. tigrbl/core/crud/helpers/enum.py +86 -0
  72. tigrbl/core/crud/helpers/filters.py +162 -0
  73. tigrbl/core/crud/helpers/model.py +123 -0
  74. tigrbl/core/crud/helpers/normalize.py +99 -0
  75. tigrbl/core/crud/ops.py +235 -0
  76. tigrbl/ddl/__init__.py +344 -0
  77. tigrbl/decorators.py +17 -0
  78. tigrbl/deps/__init__.py +20 -0
  79. tigrbl/deps/fastapi.py +45 -0
  80. tigrbl/deps/favicon.svg +4 -0
  81. tigrbl/deps/jinja.py +27 -0
  82. tigrbl/deps/pydantic.py +10 -0
  83. tigrbl/deps/sqlalchemy.py +94 -0
  84. tigrbl/deps/starlette.py +36 -0
  85. tigrbl/engine/__init__.py +26 -0
  86. tigrbl/engine/_engine.py +130 -0
  87. tigrbl/engine/bind.py +33 -0
  88. tigrbl/engine/builders.py +236 -0
  89. tigrbl/engine/collect.py +111 -0
  90. tigrbl/engine/decorators.py +108 -0
  91. tigrbl/engine/engine_spec.py +261 -0
  92. tigrbl/engine/resolver.py +224 -0
  93. tigrbl/engine/shortcuts.py +216 -0
  94. tigrbl/hook/__init__.py +21 -0
  95. tigrbl/hook/_hook.py +22 -0
  96. tigrbl/hook/decorators.py +28 -0
  97. tigrbl/hook/hook_spec.py +24 -0
  98. tigrbl/hook/mro_collect.py +98 -0
  99. tigrbl/hook/shortcuts.py +44 -0
  100. tigrbl/hook/types.py +76 -0
  101. tigrbl/op/__init__.py +50 -0
  102. tigrbl/op/_op.py +31 -0
  103. tigrbl/op/canonical.py +31 -0
  104. tigrbl/op/collect.py +11 -0
  105. tigrbl/op/decorators.py +238 -0
  106. tigrbl/op/model_registry.py +301 -0
  107. tigrbl/op/mro_collect.py +99 -0
  108. tigrbl/op/resolver.py +216 -0
  109. tigrbl/op/types.py +136 -0
  110. tigrbl/orm/__init__.py +1 -0
  111. tigrbl/orm/mixins/_RowBound.py +83 -0
  112. tigrbl/orm/mixins/__init__.py +95 -0
  113. tigrbl/orm/mixins/bootstrappable.py +113 -0
  114. tigrbl/orm/mixins/bound.py +47 -0
  115. tigrbl/orm/mixins/edges.py +40 -0
  116. tigrbl/orm/mixins/fields.py +165 -0
  117. tigrbl/orm/mixins/hierarchy.py +54 -0
  118. tigrbl/orm/mixins/key_digest.py +44 -0
  119. tigrbl/orm/mixins/lifecycle.py +115 -0
  120. tigrbl/orm/mixins/locks.py +51 -0
  121. tigrbl/orm/mixins/markers.py +16 -0
  122. tigrbl/orm/mixins/operations.py +57 -0
  123. tigrbl/orm/mixins/ownable.py +337 -0
  124. tigrbl/orm/mixins/principals.py +98 -0
  125. tigrbl/orm/mixins/tenant_bound.py +301 -0
  126. tigrbl/orm/mixins/upsertable.py +111 -0
  127. tigrbl/orm/mixins/utils.py +49 -0
  128. tigrbl/orm/tables/__init__.py +72 -0
  129. tigrbl/orm/tables/_base.py +8 -0
  130. tigrbl/orm/tables/audit.py +56 -0
  131. tigrbl/orm/tables/client.py +25 -0
  132. tigrbl/orm/tables/group.py +29 -0
  133. tigrbl/orm/tables/org.py +30 -0
  134. tigrbl/orm/tables/rbac.py +76 -0
  135. tigrbl/orm/tables/status.py +106 -0
  136. tigrbl/orm/tables/tenant.py +22 -0
  137. tigrbl/orm/tables/user.py +39 -0
  138. tigrbl/response/README.md +34 -0
  139. tigrbl/response/__init__.py +33 -0
  140. tigrbl/response/bind.py +12 -0
  141. tigrbl/response/decorators.py +37 -0
  142. tigrbl/response/resolver.py +83 -0
  143. tigrbl/response/shortcuts.py +144 -0
  144. tigrbl/response/types.py +49 -0
  145. tigrbl/rest/__init__.py +27 -0
  146. tigrbl/runtime/README.md +129 -0
  147. tigrbl/runtime/__init__.py +20 -0
  148. tigrbl/runtime/atoms/__init__.py +102 -0
  149. tigrbl/runtime/atoms/emit/__init__.py +42 -0
  150. tigrbl/runtime/atoms/emit/paired_post.py +158 -0
  151. tigrbl/runtime/atoms/emit/paired_pre.py +106 -0
  152. tigrbl/runtime/atoms/emit/readtime_alias.py +120 -0
  153. tigrbl/runtime/atoms/out/__init__.py +38 -0
  154. tigrbl/runtime/atoms/out/masking.py +135 -0
  155. tigrbl/runtime/atoms/refresh/__init__.py +38 -0
  156. tigrbl/runtime/atoms/refresh/demand.py +130 -0
  157. tigrbl/runtime/atoms/resolve/__init__.py +40 -0
  158. tigrbl/runtime/atoms/resolve/assemble.py +167 -0
  159. tigrbl/runtime/atoms/resolve/paired_gen.py +147 -0
  160. tigrbl/runtime/atoms/response/__init__.py +17 -0
  161. tigrbl/runtime/atoms/response/negotiate.py +30 -0
  162. tigrbl/runtime/atoms/response/negotiation.py +43 -0
  163. tigrbl/runtime/atoms/response/render.py +36 -0
  164. tigrbl/runtime/atoms/response/renderer.py +116 -0
  165. tigrbl/runtime/atoms/response/template.py +44 -0
  166. tigrbl/runtime/atoms/response/templates.py +88 -0
  167. tigrbl/runtime/atoms/schema/__init__.py +40 -0
  168. tigrbl/runtime/atoms/schema/collect_in.py +21 -0
  169. tigrbl/runtime/atoms/schema/collect_out.py +21 -0
  170. tigrbl/runtime/atoms/storage/__init__.py +38 -0
  171. tigrbl/runtime/atoms/storage/to_stored.py +167 -0
  172. tigrbl/runtime/atoms/wire/__init__.py +45 -0
  173. tigrbl/runtime/atoms/wire/build_in.py +166 -0
  174. tigrbl/runtime/atoms/wire/build_out.py +87 -0
  175. tigrbl/runtime/atoms/wire/dump.py +206 -0
  176. tigrbl/runtime/atoms/wire/validate_in.py +227 -0
  177. tigrbl/runtime/context.py +206 -0
  178. tigrbl/runtime/errors/__init__.py +61 -0
  179. tigrbl/runtime/errors/converters.py +214 -0
  180. tigrbl/runtime/errors/exceptions.py +124 -0
  181. tigrbl/runtime/errors/mappings.py +71 -0
  182. tigrbl/runtime/errors/utils.py +150 -0
  183. tigrbl/runtime/events.py +209 -0
  184. tigrbl/runtime/executor/__init__.py +6 -0
  185. tigrbl/runtime/executor/guards.py +132 -0
  186. tigrbl/runtime/executor/helpers.py +88 -0
  187. tigrbl/runtime/executor/invoke.py +150 -0
  188. tigrbl/runtime/executor/types.py +84 -0
  189. tigrbl/runtime/kernel.py +628 -0
  190. tigrbl/runtime/labels.py +353 -0
  191. tigrbl/runtime/opview.py +87 -0
  192. tigrbl/runtime/ordering.py +256 -0
  193. tigrbl/runtime/system.py +279 -0
  194. tigrbl/runtime/trace.py +330 -0
  195. tigrbl/schema/__init__.py +38 -0
  196. tigrbl/schema/_schema.py +27 -0
  197. tigrbl/schema/builder/__init__.py +17 -0
  198. tigrbl/schema/builder/build_schema.py +209 -0
  199. tigrbl/schema/builder/cache.py +24 -0
  200. tigrbl/schema/builder/compat.py +16 -0
  201. tigrbl/schema/builder/extras.py +85 -0
  202. tigrbl/schema/builder/helpers.py +51 -0
  203. tigrbl/schema/builder/list_params.py +117 -0
  204. tigrbl/schema/builder/strip_parent_fields.py +70 -0
  205. tigrbl/schema/collect.py +55 -0
  206. tigrbl/schema/decorators.py +68 -0
  207. tigrbl/schema/get_schema.py +86 -0
  208. tigrbl/schema/schema_spec.py +20 -0
  209. tigrbl/schema/shortcuts.py +42 -0
  210. tigrbl/schema/types.py +34 -0
  211. tigrbl/schema/utils.py +143 -0
  212. tigrbl/shortcuts.py +22 -0
  213. tigrbl/specs.py +44 -0
  214. tigrbl/system/__init__.py +12 -0
  215. tigrbl/system/diagnostics/__init__.py +24 -0
  216. tigrbl/system/diagnostics/compat.py +31 -0
  217. tigrbl/system/diagnostics/healthz.py +41 -0
  218. tigrbl/system/diagnostics/hookz.py +51 -0
  219. tigrbl/system/diagnostics/kernelz.py +20 -0
  220. tigrbl/system/diagnostics/methodz.py +43 -0
  221. tigrbl/system/diagnostics/router.py +73 -0
  222. tigrbl/system/diagnostics/utils.py +43 -0
  223. tigrbl/table/__init__.py +9 -0
  224. tigrbl/table/_base.py +237 -0
  225. tigrbl/table/_table.py +54 -0
  226. tigrbl/table/mro_collect.py +69 -0
  227. tigrbl/table/shortcuts.py +57 -0
  228. tigrbl/table/table_spec.py +28 -0
  229. tigrbl/transport/__init__.py +74 -0
  230. tigrbl/transport/jsonrpc/__init__.py +19 -0
  231. tigrbl/transport/jsonrpc/dispatcher.py +352 -0
  232. tigrbl/transport/jsonrpc/helpers.py +115 -0
  233. tigrbl/transport/jsonrpc/models.py +41 -0
  234. tigrbl/transport/rest/__init__.py +25 -0
  235. tigrbl/transport/rest/aggregator.py +132 -0
  236. tigrbl/types/__init__.py +174 -0
  237. tigrbl/types/allow_anon_provider.py +19 -0
  238. tigrbl/types/authn_abc.py +30 -0
  239. tigrbl/types/nested_path_provider.py +22 -0
  240. tigrbl/types/op.py +35 -0
  241. tigrbl/types/op_config_provider.py +17 -0
  242. tigrbl/types/op_verb_alias_provider.py +33 -0
  243. tigrbl/types/request_extras_provider.py +22 -0
  244. tigrbl/types/response_extras_provider.py +22 -0
  245. tigrbl/types/table_config_provider.py +13 -0
  246. tigrbl-0.3.0.dev3.dist-info/LICENSE +201 -0
  247. tigrbl-0.3.0.dev3.dist-info/METADATA +501 -0
  248. tigrbl-0.3.0.dev3.dist-info/RECORD +249 -0
  249. tigrbl/ExampleAgent.py +0 -1
  250. tigrbl-0.0.1.dev1.dist-info/METADATA +0 -18
  251. tigrbl-0.0.1.dev1.dist-info/RECORD +0 -5
  252. {tigrbl-0.0.1.dev1.dist-info → tigrbl-0.3.0.dev3.dist-info}/WHEEL +0 -0
tigrbl/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # Tigrbl v3 Engine Conformance
2
+
3
+ Applications built on Tigrbl v3 **must** create database engines and sessions
4
+ through the `tigrbl.engine` package. Direct imports from
5
+ `sqlalchemy.ext.asyncio`—such as `AsyncSession`, `create_async_engine`, or
6
+ `async_sessionmaker`—are **not permitted**.
7
+
8
+ Instead, construct an engine via `Engine` or the helper `engine()` function:
9
+
10
+ ```python
11
+ from tigrbl.engine import engine
12
+
13
+ DB = engine("sqlite+aiosqlite:///./app.db")
14
+ app = TigrblApp(engine=DB)
15
+ ```
16
+
17
+ Use `DB.get_db` as the FastAPI dependency for acquiring sessions and avoid
18
+ exporting custom `get_async_db` helpers.
19
+
20
+ These rules apply to all first-party applications, including
21
+ `tigrbl_kms`, `tigrbl_auth`, and the `peagen` gateway.
22
+
23
+ ## Column-Level Configuration
24
+
25
+ Tigrbl v3 models declare their database and API behavior through
26
+ `ColumnSpec` helpers exposed in `tigrbl.column`. Use `acol` for
27
+ persisted columns and `vcol` for wire-only virtual values. Each column
28
+ can combine three optional specs:
29
+
30
+ - `S` (`StorageSpec`) – database shape such as types, keys, indexes, and
31
+ other SQLAlchemy column arguments.
32
+ - `F` (`FieldSpec`) – Python and schema metadata including validation
33
+ constraints or example values.
34
+ - `IO` (`IOSpec`) – inbound/outbound exposure settings, aliases,
35
+ sensitivity flags, and filtering/sorting capabilities.
36
+
37
+ For a deeper look at these helpers, see [column/README.md](column/README.md).
38
+
39
+ Example:
40
+
41
+ ```python
42
+ from tigrbl.column import acol, vcol, F, S, IO
43
+
44
+ class Widget(Base):
45
+ __tablename__ = "widgets"
46
+
47
+ id: Mapped[int] = acol(storage=S(primary_key=True))
48
+ name: Mapped[str] = acol(
49
+ field=F(constraints={"max_length": 50}),
50
+ storage=S(nullable=False, index=True),
51
+ io=IO(
52
+ in_verbs=("create", "update"),
53
+ out_verbs=("read", "list"),
54
+ sortable=True,
55
+ ),
56
+ )
57
+ checksum: Mapped[str] = vcol(
58
+ field=F(),
59
+ io=IO(out_verbs=("read",)),
60
+ read_producer=lambda obj, ctx: f"{obj.name}:{obj.id}",
61
+ )
62
+ ```
63
+
64
+ Virtual columns like `checksum` use a `read_producer` (or `producer`)
65
+ function to compute values on the fly. Leveraging these specs keeps
66
+ column behavior declarative and consistent across the ORM, schema
67
+ generation, and runtime I/O.
68
+
69
+ ## 🧩 First-Class Object Pattern
70
+
71
+ Tigrbl v3 organizes its core building blocks with a common structure:
72
+
73
+ - 📄 **Spec** – declarative metadata describing behavior.
74
+ - 🏛️ **Class** – runtime implementation of the object.
75
+ - 🎀 **Decorators** – syntactic sugar for declaring features.
76
+ - ⚡️ **Shortcuts** – handy constructors for common setups.
77
+
78
+ Some objects also expose optional helpers:
79
+
80
+ - 🫺 **Collect** – gathers declarations from a class hierarchy.
81
+ - 🧩 **Resolver** – finalizes configuration from specs.
82
+ - 🎧 **Builder** – assembles complex runtime resources.
83
+
84
+ | Object | 📄 Spec | 🏛️ Class | 🎀 Decorators | ⚡️ Shortcuts | 🫺 Collect | 🧩 Resolver | 🎧 Builder |
85
+ |--------|----------|-----------|----------------|----------------|----------------|----------------|----------------|
86
+ | Column | `column_spec.py` | `_column.py` | — | `shortcuts.py` | `collect.py` | — | — |
87
+ | Engine | `engine_spec.py` | `_engine.py` | `decorators.py` | `shortcuts.py` | `collect.py` | `resolver.py` | `builders.py` |
88
+ | Op | `types.py` | `_op.py` | `decorators.py` | — | `collect.py` | — | — |
89
+ | API | `api_spec.py` | `_api.py` | — | `shortcuts.py` | — | — | — |
90
+ | App | `app_spec.py` | `_app.py` | — | `shortcuts.py` | — | — | — |
91
+ | Table | `table_spec.py` | `_table.py` | — | `shortcuts.py` | — | — | — |
92
+
93
+ This pattern keeps the system modular and predictable, making it easy to
94
+ discover related modules for any given concept.
tigrbl/__init__.py CHANGED
@@ -1,14 +1,139 @@
1
- from .ExampleAgent import ExampleAgent as ExampleAgent
2
-
3
- __version__ = "0.6.0.dev26"
4
- __long_desc__ = """
5
-
6
- # Swarmauri Example Plugin
7
-
8
- This repository includes an example of a Swarmauri Plugin.
9
-
10
- Visit us at: https://swarmauri.com
11
- Follow us at: https://github.com/swarmauri
12
- Star us at: https://github.com/swarmauri/swarmauri-sdk
13
-
14
- """
1
+ # tigrbl/__init__.py
2
+ """
3
+ Tigrbl public API
4
+
5
+ OpSpec-centric building blocks to bind models, wire schemas/handlers/hooks,
6
+ register RPC & REST, and (optionally) mount JSON-RPC and diagnostics.
7
+
8
+ Quick start:
9
+ from tigrbl import include_model, build_jsonrpc_router, mount_diagnostics
10
+ from tigrbl import OpSpec, hook_ctx, op_ctx, alias_ctx, schema_ctx, SchemaRef
11
+
12
+ include_model(api, User, app=fastapi_app)
13
+ app.include_router(build_jsonrpc_router(api), prefix="/rpc")
14
+ app.include_router(mount_diagnostics(api), prefix="/system")
15
+
16
+ # Example: custom op using an existing schema
17
+ @op_ctx(alias="search", target="custom", arity="collection",
18
+ request_schema=SchemaRef("Search", "in"),
19
+ response_schema=SchemaRef("Search", "out"))
20
+ def search(cls, ctx):
21
+ ...
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ # ── OpSpec (source of truth) ───────────────────────────────────────────────────
27
+ from .op import (
28
+ OpSpec,
29
+ get_registry,
30
+ # types and helpers re-exported from ops
31
+ TargetOp,
32
+ Arity,
33
+ PersistPolicy,
34
+ PHASE,
35
+ HookPhase,
36
+ PHASES,
37
+ )
38
+ from .schema.types import SchemaRef, SchemaArg
39
+
40
+ # ── Ctx-only decorators (new surface; replaces legacy ops.decorators) ─────────
41
+
42
+ from .op import alias_ctx, op_ctx, alias, op_alias
43
+ from .hook import hook_ctx
44
+ from .engine.decorators import engine_ctx
45
+ from .schema.decorators import schema_ctx
46
+ from .response.decorators import response_ctx
47
+ from .response.types import ResponseSpec
48
+
49
+ # ── Bindings (model + API orchestration) ───────────────────────────────────────
50
+ from .bindings import (
51
+ bind,
52
+ rebind,
53
+ build_schemas,
54
+ build_hooks,
55
+ build_handlers,
56
+ register_rpc,
57
+ build_rest,
58
+ include_model,
59
+ include_models,
60
+ rpc_call,
61
+ )
62
+
63
+ # ── Runtime (advanced: run a phase pipeline directly) ──────────────────────────
64
+ from .runtime.executor import _invoke
65
+
66
+ # ── Schemas ────────────────────────────────────────────────────────────────────
67
+ from .schema import _build_schema, _build_list_params, get_schema
68
+
69
+ # ── Transport & Diagnostics (optional) ─────────────────────────────────────────
70
+ from .transport.jsonrpc import build_jsonrpc_router
71
+ from .system import mount_diagnostics
72
+
73
+ # ── DB/bootstrap helpers (infra; optional) ─────────────────────────────────────
74
+ from .ddl import ensure_schemas, register_sqlite_attach, bootstrap_dbschema
75
+
76
+ # ── Config constants (defaults used by REST) ───────────────────────────────────
77
+ from .config.constants import DEFAULT_HTTP_METHODS
78
+ from .app.tigrbl_app import TigrblApp
79
+ from .api import Api, TigrblApi
80
+
81
+ from .table import Base
82
+ from .op import Op
83
+ from .app._app import App
84
+
85
+
86
+ __all__: list[str] = []
87
+
88
+ __all__ += ["TigrblApp", "TigrblApi", "Api", "Base", "App", "Op"]
89
+
90
+ __all__ += [
91
+ # OpSpec core
92
+ "OpSpec",
93
+ "get_registry",
94
+ # types
95
+ "TargetOp",
96
+ "Arity",
97
+ "PersistPolicy",
98
+ "PHASE",
99
+ "PHASES",
100
+ "HookPhase",
101
+ "SchemaRef",
102
+ "SchemaArg",
103
+ # Ctx-only decorators
104
+ "alias_ctx",
105
+ "op_ctx",
106
+ "hook_ctx",
107
+ "schema_ctx",
108
+ "response_ctx",
109
+ "alias",
110
+ "op_alias",
111
+ "engine_ctx",
112
+ "ResponseSpec",
113
+ # Bindings
114
+ "bind",
115
+ "rebind",
116
+ "build_schemas",
117
+ "build_hooks",
118
+ "build_handlers",
119
+ "register_rpc",
120
+ "build_rest",
121
+ "include_model",
122
+ "include_models",
123
+ "rpc_call",
124
+ # Runtime
125
+ "_invoke",
126
+ # Schemas
127
+ "_build_schema",
128
+ "_build_list_params",
129
+ "get_schema",
130
+ # Transport / Diagnostics
131
+ "build_jsonrpc_router",
132
+ "mount_diagnostics",
133
+ # DB/infra
134
+ "ensure_schemas",
135
+ "register_sqlite_attach",
136
+ "bootstrap_dbschema",
137
+ # Config
138
+ "DEFAULT_HTTP_METHODS",
139
+ ]
tigrbl/api/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Core API surfaces for the Tigrbl package."""
2
+
3
+ from ._api import Api
4
+ from .tigrbl_api import TigrblApi
5
+
6
+ __all__ = ["Api", "TigrblApi"]
tigrbl/api/_api.py ADDED
@@ -0,0 +1,72 @@
1
+ # tigrbl/tigrbl/v3/api/_api.py
2
+ from __future__ import annotations
3
+ from typing import Any
4
+ from types import SimpleNamespace
5
+
6
+ from ..deps.fastapi import APIRouter as ApiRouter
7
+ from ..engine.engine_spec import EngineCfg
8
+ from ..engine import install_from_objects
9
+ from ..engine import resolver as _resolver
10
+ from .api_spec import APISpec
11
+
12
+
13
+ class Api(APISpec, ApiRouter):
14
+ """API router with model and table registries."""
15
+
16
+ MODELS: tuple[Any, ...] = ()
17
+ TABLES: tuple[Any, ...] = ()
18
+
19
+ # dataclass inheritance makes instances unhashable; use identity semantics
20
+ # for both hashing and equality so objects can participate in sets/dicts
21
+ def __hash__(self) -> int: # pragma: no cover - simple identity hash
22
+ return id(self)
23
+
24
+ def __eq__(self, other: object) -> bool: # pragma: no cover - identity compare
25
+ return self is other
26
+
27
+ def __init__(
28
+ self, *, engine: EngineCfg | None = None, **router_kwargs: Any
29
+ ) -> None:
30
+ # Manually initialize fields from ``APISpec`` so ``repr`` and other
31
+ # dataclass-generated helpers have the expected attributes, while also
32
+ # preparing mutable containers used at runtime.
33
+ self.name = getattr(self, "NAME", "api")
34
+ self.prefix = self.PREFIX
35
+ self.engine = engine if engine is not None else getattr(self, "ENGINE", None)
36
+ self.tags = list(getattr(self, "TAGS", []))
37
+ self.ops = tuple(getattr(self, "OPS", ()))
38
+ self.schemas = SimpleNamespace()
39
+ self.hooks = SimpleNamespace()
40
+ self.security_deps = tuple(getattr(self, "SECURITY_DEPS", ()))
41
+ self.deps = tuple(getattr(self, "DEPS", ()))
42
+ self.response = getattr(self, "RESPONSE", None)
43
+ # ``models`` is expected to be a dict at runtime for registry lookups.
44
+ self.models: dict[str, type] = {}
45
+
46
+ ApiRouter.__init__(
47
+ self,
48
+ prefix=self.PREFIX,
49
+ tags=self.tags,
50
+ dependencies=list(self.security_deps) + list(self.deps),
51
+ **router_kwargs,
52
+ )
53
+
54
+ # namespace containers
55
+ self.tables: dict[str, Any] = {}
56
+
57
+ _engine_ctx = engine if engine is not None else getattr(self, "ENGINE", None)
58
+ if _engine_ctx is not None:
59
+ _resolver.register_api(self, _engine_ctx)
60
+ _resolver.resolve_provider(api=self)
61
+
62
+ def install_engines(
63
+ self, *, api: Any = None, models: tuple[Any, ...] | None = None
64
+ ) -> None:
65
+ # If class declared APIS/MODELS, use them unless explicit args are passed.
66
+ apis = (api,) if api is not None else self.APIS
67
+ models = models if models is not None else self.MODELS
68
+ if apis:
69
+ for a in apis:
70
+ install_from_objects(app=self, api=a, models=models)
71
+ else:
72
+ install_from_objects(app=self, api=None, models=models)
tigrbl/api/api_spec.py ADDED
@@ -0,0 +1,30 @@
1
+ # tigrbl/tigrbl/v3/api/api_spec.py
2
+ from __future__ import annotations
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Callable, Optional, Sequence
5
+ from ..engine.engine_spec import EngineCfg
6
+ from ..response.types import ResponseSpec
7
+
8
+
9
+ @dataclass
10
+ class APISpec:
11
+ """
12
+ Used to *produce an API subclass* via API.from_spec().
13
+ """
14
+
15
+ name: str = "api"
16
+ prefix: str = ""
17
+ engine: Optional[EngineCfg] = None
18
+ tags: Sequence[str] = field(default_factory=tuple)
19
+
20
+ # NEW
21
+ ops: Sequence[Any] = field(default_factory=tuple)
22
+ schemas: Sequence[Any] = field(default_factory=tuple)
23
+ hooks: Sequence[Callable[..., Any]] = field(default_factory=tuple)
24
+ security_deps: Sequence[Callable[..., Any]] = field(default_factory=tuple)
25
+ deps: Sequence[Callable[..., Any]] = field(default_factory=tuple)
26
+
27
+ response: Optional[ResponseSpec] = None
28
+
29
+ # optional: models this API exposes (auto-install)
30
+ models: Sequence[Any] = field(default_factory=tuple)
@@ -0,0 +1,43 @@
1
+ # tigrbl/v3/api/mro_collect.py
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ from functools import lru_cache
6
+ from typing import Any, Callable, Dict, Iterable, Mapping
7
+
8
+ from ..config.constants import TIGRBL_API_HOOKS_ATTR
9
+
10
+ logger = logging.getLogger("uvicorn")
11
+
12
+
13
+ @lru_cache(maxsize=None)
14
+ def mro_collect_api_hooks(api: type) -> Dict[str, Dict[str, list[Callable[..., Any]]]]:
15
+ """Collect API-level hook declarations across ``api``'s MRO.
16
+
17
+ The accepted shape mirrors the hooks mapping used by the bindings:
18
+ {alias: {phase: Iterable[callable]}}
19
+ Hooks from base classes are merged with subclass definitions taking precedence.
20
+ """
21
+ logger.info("Collecting API hooks for %s", api.__name__)
22
+ out: Dict[str, Dict[str, list[Callable[..., Any]]]] = {}
23
+ for base in reversed(api.__mro__):
24
+ mapping = getattr(base, TIGRBL_API_HOOKS_ATTR, None)
25
+ if not isinstance(mapping, Mapping):
26
+ continue
27
+ for alias, phase_map in mapping.items():
28
+ bucket = out.setdefault(str(alias), {})
29
+ if not isinstance(phase_map, Mapping):
30
+ continue
31
+ for phase, items in phase_map.items():
32
+ lst = bucket.setdefault(str(phase), [])
33
+ if isinstance(items, Iterable):
34
+ for fn in items:
35
+ if callable(fn):
36
+ lst.append(fn)
37
+ elif callable(items):
38
+ lst.append(items)
39
+ logger.debug("Collected API hooks for aliases: %s", list(out.keys()))
40
+ return out
41
+
42
+
43
+ __all__ = ["mro_collect_api_hooks"]
@@ -0,0 +1,56 @@
1
+ # tigrbl/tigrbl/v3/api/shortcuts.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Sequence, Type
5
+
6
+ from .api_spec import APISpec
7
+ from ._api import Api
8
+
9
+
10
+ def defineApiSpec(
11
+ *,
12
+ # identity
13
+ name: str = "api",
14
+ prefix: str = "",
15
+ tags: Sequence[str] = (),
16
+ # engine
17
+ engine: Any = None,
18
+ # composition
19
+ ops: Sequence[Any] = (),
20
+ schemas: Sequence[Any] = (),
21
+ hooks: Sequence[Any] = (),
22
+ security_deps: Sequence[Any] = (),
23
+ deps: Sequence[Any] = (),
24
+ models: Sequence[Any] = (),
25
+ ) -> Type[APISpec]:
26
+ """
27
+ Build an API-spec class with class attributes only (no instances).
28
+ Use it directly in your class MRO:
29
+
30
+ class TenantA(defineApiSpec(name="tenantA", engine=...)):
31
+ pass
32
+
33
+ or pass it to `deriveApi(...)` to get a concrete API subclass.
34
+ """
35
+ attrs = dict(
36
+ NAME=name,
37
+ PREFIX=prefix,
38
+ TAGS=tuple(tags or ()),
39
+ ENGINE=engine,
40
+ OPS=tuple(ops or ()),
41
+ SCHEMAS=tuple(schemas or ()),
42
+ HOOKS=tuple(hooks or ()),
43
+ SECURITY_DEPS=tuple(security_deps or ()),
44
+ DEPS=tuple(deps or ()),
45
+ MODELS=tuple(models or ()),
46
+ )
47
+ return type("APISpec", (APISpec,), attrs)
48
+
49
+
50
+ def deriveApi(**kw: Any) -> Type[Api]:
51
+ """Produce a concrete :class:`Api` subclass that inherits the spec."""
52
+ Spec = defineApiSpec(**kw)
53
+ return type("APIWithSpec", (Spec, Api), {})
54
+
55
+
56
+ __all__ = ["defineApiSpec", "deriveApi"]