tigrbl_engine_rom 0.4.5__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.
@@ -0,0 +1,18 @@
1
+ """Read-only memory engine for Tigrbl."""
2
+
3
+ from .engine import ROMEngine
4
+ from .plugin import build_rom, capabilities, register
5
+ from .session import READ_ONLY_ERROR, ROMResult, ROMSession
6
+
7
+ __all__ = [
8
+ "READ_ONLY_ERROR",
9
+ "ROMEngine",
10
+ "ROMResult",
11
+ "ROMSession",
12
+ "build_rom",
13
+ "capabilities",
14
+ "register",
15
+ "__version__",
16
+ ]
17
+
18
+ __version__ = "0.4.5.dev4"
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from types import MappingProxyType
5
+ from typing import Any, Iterable, Mapping
6
+
7
+ from tigrbl_base._base import EngineBase
8
+
9
+
10
+ def _freeze(value: Any) -> Any:
11
+ """Return an immutable, detached representation of configuration data."""
12
+ if isinstance(value, Mapping):
13
+ return MappingProxyType({key: _freeze(item) for key, item in value.items()})
14
+ if isinstance(value, (list, tuple)):
15
+ return tuple(_freeze(item) for item in value)
16
+ if isinstance(value, (set, frozenset)):
17
+ return frozenset(_freeze(item) for item in value)
18
+ return deepcopy(value)
19
+
20
+
21
+ def _thaw(value: Any) -> Any:
22
+ """Return a caller-owned copy of a frozen value."""
23
+ if isinstance(value, Mapping):
24
+ return {key: _thaw(item) for key, item in value.items()}
25
+ if isinstance(value, tuple):
26
+ return [_thaw(item) for item in value]
27
+ if isinstance(value, frozenset):
28
+ return {_thaw(item) for item in value}
29
+ return deepcopy(value)
30
+
31
+
32
+ class ROMEngine(EngineBase):
33
+ """Process-local immutable row image bound as an input to one table."""
34
+
35
+ def __init__(
36
+ self,
37
+ rows: Iterable[Mapping[str, Any]] | None = None,
38
+ *,
39
+ primary_key: str = "id",
40
+ namespace: str = "default",
41
+ spec: Any = None,
42
+ ) -> None:
43
+ if not isinstance(namespace, str) or not namespace:
44
+ raise ValueError("namespace must be a non-empty string")
45
+ if not isinstance(primary_key, str) or not primary_key:
46
+ raise ValueError("primary_key must be a non-empty string")
47
+ self.namespace = namespace
48
+ self.spec = spec
49
+ self.primary_key = primary_key
50
+ frozen_rows: list[Mapping[str, Any]] = []
51
+ index: dict[Any, Mapping[str, Any]] = {}
52
+ for position, source_row in enumerate(rows or ()):
53
+ if not isinstance(source_row, Mapping):
54
+ raise TypeError(f"row {position} must be a mapping")
55
+ if primary_key not in source_row:
56
+ raise ValueError(
57
+ f"row {position} is missing primary key {primary_key!r}"
58
+ )
59
+ key = deepcopy(source_row[primary_key])
60
+ try:
61
+ duplicate = key in index
62
+ except TypeError as exc:
63
+ raise TypeError(
64
+ f"primary key {primary_key!r} must be hashable"
65
+ ) from exc
66
+ if duplicate:
67
+ raise ValueError(f"duplicate primary key {key!r}")
68
+ frozen_row = _freeze(source_row)
69
+ frozen_rows.append(frozen_row)
70
+ index[key] = frozen_row
71
+
72
+ self._rows = tuple(frozen_rows)
73
+ self._index = MappingProxyType(index)
74
+
75
+ def to_provider(self) -> "ROMEngine":
76
+ """Expose this immutable engine as its own provider-like object."""
77
+ return self
78
+
79
+ async def _executeloop_impl(self, statements: Any) -> Any:
80
+ from .session import ROMSession
81
+
82
+ session = ROMSession(self)
83
+ try:
84
+ return await session.executeloop(statements)
85
+ finally:
86
+ await session.close()
87
+
88
+ async def _executemany_impl(self, stmt: Any, parameter_sets: Any) -> Any:
89
+ from .session import ROMSession
90
+
91
+ session = ROMSession(self)
92
+ try:
93
+ return await session.executemany(stmt, parameter_sets)
94
+ finally:
95
+ await session.close()
96
+
97
+ def get(self, ident: Any) -> dict[str, Any] | None:
98
+ row = self._index.get(ident)
99
+ return None if row is None else _thaw(row)
100
+
101
+ def scan(self) -> list[dict[str, Any]]:
102
+ return [_thaw(row) for row in self._rows]
103
+
104
+
105
+ __all__ = ["ROMEngine"]
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from tigrbl.engine.registry import register_engine
6
+
7
+ from .engine import ROMEngine
8
+ from .session import ROMSession
9
+
10
+
11
+ def capabilities(*, spec: Any = None, mapping: Any = None) -> dict[str, Any]:
12
+ return {
13
+ "engine": "rom",
14
+ "transactional": False,
15
+ "async_native": True,
16
+ "isolation_levels": set(),
17
+ "read_only_enforced": True,
18
+ "persistence": "process",
19
+ "storage": "immutable-memory",
20
+ "read_consistency": "immutable-snapshot",
21
+ "binding_scopes": {"table"},
22
+ }
23
+
24
+
25
+ def build_rom(*, mapping=None, spec=None, dsn=None, **_: Any) -> tuple[object, object]:
26
+ if dsn:
27
+ raise ValueError("ROM engine accepts embedded mapping data, not a DSN")
28
+ config = dict(mapping or {})
29
+ engine = ROMEngine(
30
+ rows=config.get("rows"),
31
+ primary_key=str(config.get("primary_key", "id")),
32
+ namespace=str(config.get("namespace", "default")),
33
+ spec=spec,
34
+ )
35
+
36
+ def sessionmaker(session_spec=None) -> ROMSession:
37
+ return ROMSession(engine, spec=session_spec)
38
+
39
+ return engine, sessionmaker
40
+
41
+
42
+ class _Registration:
43
+ def build(self, *, mapping, spec, dsn):
44
+ return build_rom(mapping=mapping, spec=spec, dsn=dsn)
45
+
46
+ def capabilities(self, *, spec, mapping=None):
47
+ return capabilities(spec=spec, mapping=mapping)
48
+
49
+
50
+ def register() -> None:
51
+ register_engine("rom", _Registration())
52
+
53
+
54
+ __all__ = ["build_rom", "capabilities", "register"]
@@ -0,0 +1,227 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from dataclasses import replace
5
+ from typing import Any, Callable, Iterable, Mapping, Sequence
6
+
7
+ from tigrbl_base._base import EngineSessionBase
8
+ from tigrbl_core._spec.engine_session_spec import EngineSessionSpec
9
+
10
+ from .engine import ROMEngine
11
+
12
+
13
+ READ_ONLY_ERROR = "write attempted against read-only memory engine"
14
+
15
+
16
+ class ROMResult:
17
+ """Small result facade compatible with Tigrbl's scalar query helpers."""
18
+
19
+ def __init__(self, items: Sequence[Any]) -> None:
20
+ self._items = tuple(items)
21
+ self.rowcount = len(self._items)
22
+
23
+ def scalars(self) -> "ROMResult":
24
+ return self
25
+
26
+ def all(self) -> list[Any]:
27
+ return list(self._items)
28
+
29
+ def first(self) -> Any | None:
30
+ return self._items[0] if self._items else None
31
+
32
+ def scalar_one(self) -> Any:
33
+ if len(self._items) != 1:
34
+ raise RuntimeError(f"expected exactly one row, found {len(self._items)}")
35
+ return self._items[0]
36
+
37
+
38
+ class ROMSession(EngineSessionBase):
39
+ """Async Tigrbl session over an immutable :class:`ROMEngine` snapshot."""
40
+
41
+ def __init__(
42
+ self, engine: ROMEngine, spec: EngineSessionSpec | None = None
43
+ ) -> None:
44
+ enforced = replace(spec or EngineSessionSpec(), read_only=True)
45
+ super().__init__(enforced)
46
+ self._engine = engine
47
+ self._closed = False
48
+ self._tracked: dict[int, tuple[Any, dict[str, Any]]] = {}
49
+
50
+ def apply_spec(self, spec: EngineSessionSpec | None) -> None:
51
+ enforced = replace(spec or EngineSessionSpec(), read_only=True)
52
+ super().apply_spec(enforced)
53
+
54
+ def get_row(self, ident: Any) -> dict[str, Any] | None:
55
+ self._require_open_session()
56
+ return self._engine.get(ident)
57
+
58
+ def query_rows(
59
+ self,
60
+ predicate: Callable[[Mapping[str, Any]], bool] | None = None,
61
+ ) -> list[dict[str, Any]]:
62
+ self._require_open_session()
63
+ rows = self._engine.scan()
64
+ if predicate is not None:
65
+ rows = [row for row in rows if predicate(row)]
66
+ return rows
67
+
68
+ async def _tx_begin_impl(self) -> None:
69
+ self._require_open_session()
70
+
71
+ async def _tx_commit_impl(self) -> None:
72
+ self._require_open_session()
73
+
74
+ async def _tx_rollback_impl(self) -> None:
75
+ self._require_open_session()
76
+ for obj, snapshot in self._tracked.values():
77
+ for name, value in snapshot.items():
78
+ setattr(obj, name, deepcopy(value))
79
+
80
+ def _add_impl(self, obj: Any) -> None:
81
+ raise RuntimeError(READ_ONLY_ERROR)
82
+
83
+ async def _delete_impl(self, obj: Any) -> None:
84
+ raise RuntimeError(READ_ONLY_ERROR)
85
+
86
+ async def _flush_impl(self) -> None:
87
+ self._require_open_session()
88
+ for obj, snapshot in self._tracked.values():
89
+ if self._snapshot(obj) != snapshot:
90
+ raise RuntimeError(READ_ONLY_ERROR)
91
+
92
+ async def _refresh_impl(self, obj: Any) -> None:
93
+ self._require_open_session()
94
+ row = self._engine.get(getattr(obj, self._engine.primary_key))
95
+ if row is None:
96
+ return
97
+ for name, value in row.items():
98
+ setattr(obj, name, value)
99
+ self._track(obj)
100
+
101
+ async def _get_impl(self, model: type, ident: Any) -> Any | None:
102
+ self._require_open_session()
103
+ row = self._engine.get(ident)
104
+ return None if row is None else self._inflate(model, row)
105
+
106
+ async def _execute_impl(self, stmt: Any) -> ROMResult:
107
+ self._require_open_session()
108
+ if type(stmt).__name__.lower() != "select":
109
+ raise RuntimeError(READ_ONLY_ERROR)
110
+ model = self._extract_model(stmt)
111
+ items = [self._inflate(model, row) for row in self._engine.scan()]
112
+ for name, operator, value in self._extract_predicates(stmt):
113
+ if operator == "eq":
114
+ items = [item for item in items if getattr(item, name, None) == value]
115
+ elif operator == "in":
116
+ accepted = set(value)
117
+ items = [
118
+ item for item in items if getattr(item, name, None) in accepted
119
+ ]
120
+ else: # pragma: no cover - protected by extraction
121
+ raise NotImplementedError(f"unsupported ROM predicate: {operator}")
122
+ for name, descending in reversed(self._extract_ordering(stmt)):
123
+ items.sort(key=lambda item: getattr(item, name, None), reverse=descending)
124
+ offset = self._clause_int(getattr(stmt, "_offset_clause", None)) or 0
125
+ limit = self._clause_int(getattr(stmt, "_limit_clause", None))
126
+ items = items[max(0, offset) :]
127
+ if limit is not None:
128
+ items = items[: max(0, limit)]
129
+ return ROMResult(items)
130
+
131
+ async def _executeloop_impl(self, statements: Iterable[Any]) -> list[ROMResult]:
132
+ return [await self._execute_impl(statement) for statement in statements]
133
+
134
+ async def _executemany_impl(self, stmt: Any, parameter_sets: Any) -> Any:
135
+ raise RuntimeError(
136
+ "parameterized batch execution is unavailable on read-only memory engine"
137
+ )
138
+
139
+ async def _close_impl(self) -> None:
140
+ self._closed = True
141
+ self._open = False
142
+ self._dirty = False
143
+ self._tracked.clear()
144
+
145
+ def _require_open_session(self) -> None:
146
+ if self._closed:
147
+ raise RuntimeError("ROM session is closed")
148
+
149
+ def _inflate(self, model: type, row: Mapping[str, Any]) -> Any:
150
+ try:
151
+ obj = model(**row)
152
+ except TypeError:
153
+ obj = model()
154
+ for name, value in row.items():
155
+ setattr(obj, name, value)
156
+ self._track(obj)
157
+ return obj
158
+
159
+ def _track(self, obj: Any) -> None:
160
+ self._tracked[id(obj)] = (obj, self._snapshot(obj))
161
+
162
+ @staticmethod
163
+ def _snapshot(obj: Any) -> dict[str, Any]:
164
+ return {
165
+ name: deepcopy(value)
166
+ for name, value in vars(obj).items()
167
+ if not name.startswith("_sa_")
168
+ }
169
+
170
+ @staticmethod
171
+ def _extract_model(stmt: Any) -> type:
172
+ for description in getattr(stmt, "column_descriptions", ()):
173
+ entity = description.get("entity")
174
+ if isinstance(entity, type):
175
+ return entity
176
+ raise NotImplementedError("ROM selects must target one mapped model")
177
+
178
+ @classmethod
179
+ def _extract_predicates(cls, stmt: Any) -> list[tuple[str, str, Any]]:
180
+ predicates: list[tuple[str, str, Any]] = []
181
+ for criterion in getattr(stmt, "_where_criteria", ()):
182
+ clauses = getattr(criterion, "clauses", None)
183
+ if clauses is not None:
184
+ boolean_operator = getattr(
185
+ getattr(criterion, "operator", None), "__name__", ""
186
+ )
187
+ if boolean_operator != "and_":
188
+ raise NotImplementedError(
189
+ "ROM supports conjunctions of equality and IN predicates only"
190
+ )
191
+ for clause in clauses:
192
+ predicates.extend(cls._extract_binary_predicate(clause))
193
+ else:
194
+ predicates.extend(cls._extract_binary_predicate(criterion))
195
+ return predicates
196
+
197
+ @staticmethod
198
+ def _extract_binary_predicate(expr: Any) -> list[tuple[str, str, Any]]:
199
+ name = getattr(getattr(expr, "left", None), "key", None)
200
+ operator_name = getattr(getattr(expr, "operator", None), "__name__", "")
201
+ right = getattr(expr, "right", None)
202
+ value = getattr(right, "effective_value", getattr(right, "value", None))
203
+ if not name or operator_name not in {"eq", "in_op"}:
204
+ raise NotImplementedError("ROM supports equality and IN predicates only")
205
+ return [(name, "in" if operator_name == "in_op" else "eq", value)]
206
+
207
+ @staticmethod
208
+ def _extract_ordering(stmt: Any) -> list[tuple[str, bool]]:
209
+ ordering: list[tuple[str, bool]] = []
210
+ for clause in getattr(stmt, "_order_by_clauses", ()):
211
+ element = getattr(clause, "element", clause)
212
+ name = getattr(element, "key", None)
213
+ if not name:
214
+ raise NotImplementedError("ROM ordering must target mapped columns")
215
+ modifier = getattr(getattr(clause, "modifier", None), "__name__", "")
216
+ ordering.append((name, modifier == "desc_op"))
217
+ return ordering
218
+
219
+ @staticmethod
220
+ def _clause_int(clause: Any) -> int | None:
221
+ if clause is None:
222
+ return None
223
+ value = getattr(clause, "effective_value", getattr(clause, "value", clause))
224
+ return int(value)
225
+
226
+
227
+ __all__ = ["READ_ONLY_ERROR", "ROMResult", "ROMSession"]
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.5
2
+ Name: tigrbl_engine_rom
3
+ Version: 0.4.5
4
+ Summary: Immutable read-only memory engine plugin for Tigrbl.
5
+ Project-URL: Homepage, https://github.com/tigrbl/tigrbl
6
+ Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/90_engines/tigrbl_engine_rom
7
+ Project-URL: Issues, https://github.com/tigrbl/tigrbl/issues
8
+ Author-email: Jacob Stewart <jacob@swarmauri.com>
9
+ License: Apache License
10
+ Version 2.0, January 2004
11
+ https://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Copyright 2026 Swarmauri
14
+
15
+ Licensed under the Apache License, Version 2.0 (the "License");
16
+ you may not use this file except in compliance with the License.
17
+ You may obtain a copy of the License at
18
+
19
+ https://www.apache.org/licenses/LICENSE-2.0
20
+
21
+ Unless required by applicable law or agreed to in writing, software
22
+ distributed under the License is distributed on an "AS IS" BASIS,
23
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
+ See the License for the specific language governing permissions and
25
+ limitations under the License.
26
+ License-File: LICENSE
27
+ License-File: NOTICE
28
+ Keywords: engine,immutable,memory,plugin,readonly,tigrbl
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Framework :: AsyncIO
31
+ Classifier: License :: OSI Approved :: Apache Software License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3 :: Only
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
+ Classifier: Programming Language :: Python :: 3.14
40
+ Classifier: Topic :: Database
41
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
42
+ Requires-Python: <3.15,>=3.10
43
+ Requires-Dist: tigrbl>=0.3.0.dev4
44
+ Description-Content-Type: text/markdown
45
+
46
+ <div align="center">
47
+ <h1>tigrbl_engine_rom</h1>
48
+ <p><strong>Immutable read-only memory engine plugin for Tigrbl.</strong></p>
49
+ <a href="https://pypi.org/project/tigrbl_engine_rom/"><img src="https://img.shields.io/pypi/v/tigrbl_engine_rom?label=PyPI" alt="PyPI version"/></a>
50
+ <a href="https://discord.gg/K4YTAPapjR"><img src="https://img.shields.io/badge/Discord-Join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a>
51
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-525252" alt="Apache 2.0 license"/></a>
52
+ <a href="pyproject.toml"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-3776ab" alt="Python 3.10 | 3.11 | 3.12 | 3.13 | 3.14"/></a>
53
+ </div>
54
+
55
+ ## What is tigrbl_engine_rom?
56
+
57
+ `tigrbl_engine_rom` loads one mapping-backed row image and serves it through Tigrbl's engine-session contract. The engine configuration is bound as an input to a table.
58
+
59
+ ## Why use tigrbl_engine_rom?
60
+
61
+ Use it for embedded catalogs, fixtures, policy data, reference tables, and other datasets that must never change at runtime.
62
+
63
+ ## When should I install tigrbl_engine_rom?
64
+
65
+ Install it when your application needs fast reads from a fixed memory image and should fail closed on every write.
66
+
67
+ ## Who is tigrbl_engine_rom for?
68
+
69
+ It is for application developers shipping trusted reference data or deterministic read-only test images.
70
+
71
+ ## Where does tigrbl_engine_rom fit?
72
+
73
+ It is a standalone package under `pkgs/90_engines` and is discovered through the `tigrbl.engine` entry-point group as kind `rom`.
74
+
75
+ ## How does tigrbl_engine_rom work?
76
+
77
+ `ROMEngine` subclasses Tigrbl's `EngineBase`. Construction validates the bound table's rows and primary key, deeply detaches the source data, and freezes it. Reads always return caller-owned copies. Sessions force `read_only=True`, even if a caller requests otherwise.
78
+
79
+ ## Install
80
+
81
+ ```bash
82
+ uv add tigrbl_engine_rom
83
+ ```
84
+
85
+ ## Surface Coverage
86
+
87
+ | Surface | Value |
88
+ |---|---|
89
+ | Engine kind | `rom` |
90
+ | Python import | `tigrbl_engine_rom` |
91
+ | Entry point | `tigrbl.engine` |
92
+ | Persistence | process-local immutable memory |
93
+ | Supported Python | `3.10 | 3.11 | 3.12 | 3.13 | 3.14` |
94
+
95
+ ## What It Owns
96
+
97
+ The package owns the ROM image, read-only session, plugin registration, and scalar result facade.
98
+
99
+ ## Public API and Import Surface
100
+
101
+ Public symbols are `ROMEngine`, `ROMSession`, `ROMResult`, `build_rom`, `capabilities`, and `register`.
102
+
103
+ ## Usage Examples
104
+
105
+ ```python
106
+ from tigrbl_engine_rom import build_rom
107
+
108
+ engine, sessionmaker = build_rom(mapping={
109
+ "rows": [
110
+ {"code": "US", "name": "United States"},
111
+ {"code": "CA", "name": "Canada"},
112
+ ],
113
+ "primary_key": "code",
114
+ })
115
+
116
+ session = sessionmaker()
117
+ assert session.get_row("CA")["name"] == "Canada"
118
+ ```
119
+
120
+ Bind the engine configuration into a table:
121
+
122
+ ```python
123
+ from tigrbl.factories.table import defineTableSpec
124
+
125
+ class CountrySpec(defineTableSpec(engine={
126
+ "kind": "rom",
127
+ "rows": [
128
+ {"code": "US", "name": "United States"},
129
+ {"code": "CA", "name": "Canada"},
130
+ ],
131
+ "primary_key": "code",
132
+ })):
133
+ pass
134
+ ```
135
+
136
+ ## How To Choose This Package
137
+
138
+ Choose ROM for immutable embedded data. Choose `tigrbl_engine_inmemory` when transactions and runtime writes are required.
139
+
140
+ ## Related Packages
141
+
142
+ - `tigrbl`
143
+ - `tigrbl_engine_inmemory`
144
+ - `tigrbl_engine_memkv`
145
+
146
+ ## Documentation Links
147
+
148
+ - [Workspace docs](../../../docs/README.md)
149
+ - [Package catalog](../../../docs/developer/PACKAGE_CATALOG.md)
150
+ - [SSOT registry](../../../.ssot/registry.json)
151
+
152
+ ## Support
153
+
154
+ - [Discord](https://discord.gg/K4YTAPapjR)
155
+ - [GitHub issues](https://github.com/tigrbl/tigrbl/issues)
156
+
157
+ ## Certification Status
158
+
159
+ The package is implemented with package-local behavioral tests. Certification remains governed by the repository SSOT registry and release workflows.
160
+
161
+ ## License
162
+
163
+ Licensed under the Apache License, Version 2.0. See `LICENSE` and `NOTICE`.
164
+
165
+ ## Workspace documentation
166
+
167
+ This README is the package-local distribution entry point. See [workspace documentation](../../../docs/README.md), [current conformance target](../../../docs/conformance/CURRENT_TARGET.md), [package catalog](../../../docs/developer/PACKAGE_CATALOG.md), [package layout](../../../docs/developer/PACKAGE_LAYOUT.md), and [SSOT registry](../../../.ssot/registry.json).
@@ -0,0 +1,10 @@
1
+ tigrbl_engine_rom/__init__.py,sha256=-Vo3g8X_WlICo9l0bSAhqSeBzRUDcxUqvJsO3tjjdpg,377
2
+ tigrbl_engine_rom/engine.py,sha256=TIThltMj8idUPMrpl68iAkZBqZKvRnKvF83eMZ6HLT8,3682
3
+ tigrbl_engine_rom/plugin.py,sha256=E9oDh0_msL4F3xNqpa5wZIs2WGTPkMO4omXT8VYmfoM,1508
4
+ tigrbl_engine_rom/session.py,sha256=k342vQTAq0-TNpnisyTt0lW_vghQYbULKxrV2P5ZdqA,8654
5
+ tigrbl_engine_rom-0.4.5.dist-info/METADATA,sha256=RIrazWk42f5ohr2DD0m_TfS2ReolmtWLAjyYAE-medo,6317
6
+ tigrbl_engine_rom-0.4.5.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
7
+ tigrbl_engine_rom-0.4.5.dist-info/entry_points.txt,sha256=KHczrTslM6l70hEoKETsz4bdq4bqsMMIX5Sg3L5xJTE,64
8
+ tigrbl_engine_rom-0.4.5.dist-info/licenses/LICENSE,sha256=Z59l_PeraPrA4KbufxEZ0ft3xquZACI-vxI-4RYLp54,637
9
+ tigrbl_engine_rom-0.4.5.dist-info/licenses/NOTICE,sha256=_I9mpUItIZUweT3-Ypbwn3Cdjdqfpm7v_P6b9CwYX4c,222
10
+ tigrbl_engine_rom-0.4.5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [tigrbl.engine_plugins]
2
+ rom = tigrbl_engine_rom.plugin:register
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ https://www.apache.org/licenses/LICENSE-2.0
4
+
5
+ Copyright 2026 Swarmauri
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ https://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,7 @@
1
+ Tigrbl
2
+ Copyright 2026 Swarmauri
3
+
4
+ This product includes software developed by Swarmauri.
5
+
6
+ Tigrbl is licensed under the Apache License, Version 2.0.
7
+ See the LICENSE file distributed with this work for the full license text.