etlantic-sqlmodel 0.55.0__tar.gz → 0.56.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.
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/PKG-INFO +6 -6
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/README.md +4 -4
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/pyproject.toml +2 -2
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/__init__.py +1 -1
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/__init__.py +14 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/cp4_stores.py +103 -23
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/control_plane/durable_stores.py +660 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/control_plane/input_resource_stores.py +650 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/models.py +91 -1
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/registry_stores.py +195 -4
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/control_plane/report_stores.py +238 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/schedule_stores.py +82 -8
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/control_plane/stores.py +917 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/__init__.py +9 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/006_managed_definition_revisions_0_56.py +123 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/007_managed_run_reports_0_56.py +27 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/008_idempotent_run_events_0_56.py +54 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/009_event_retention_tombstones_0_56.py +99 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/010_immutable_input_resources_0_56.py +90 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/011_run_artifact_retention_0_56.py +136 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/012_bounded_event_tombstone_retention_0_56.py +106 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/013_durable_submission_scope_backfill_0_56.py +105 -0
- etlantic_sqlmodel-0.56.0/src/etlantic_sqlmodel/migrations/versions/014_cp1_complete_principal_idempotency_0_56.py +326 -0
- etlantic_sqlmodel-0.55.0/src/etlantic_sqlmodel/control_plane/durable_stores.py +0 -366
- etlantic_sqlmodel-0.55.0/src/etlantic_sqlmodel/control_plane/stores.py +0 -502
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/.gitignore +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/registry_backup.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/registry_search.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/control_plane/session.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/001_registry_cp2.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/002_durable_cp3.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/003_cp4_governance.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/004_schedules_0_47.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/005_cp1_reference.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/migrations/versions/__init__.py +0 -0
- {etlantic_sqlmodel-0.55.0 → etlantic_sqlmodel-0.56.0}/src/etlantic_sqlmodel/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: etlantic-sqlmodel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.56.0
|
|
4
4
|
Summary: ContractModel ↔ SQLModel bridge for ETLantic.
|
|
5
5
|
Project-URL: Homepage, https://github.com/eddiethedean/etlantic
|
|
6
6
|
Project-URL: Documentation, https://github.com/eddiethedean/etlantic/tree/main/docs
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Typing :: Typed
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
|
-
Requires-Dist: etlantic<0.
|
|
21
|
+
Requires-Dist: etlantic<0.57,>=0.56.0
|
|
22
22
|
Requires-Dist: sqlmodel<1,>=0.0.22
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
@@ -28,13 +28,13 @@ Optional bridge between ETLantic `Data` contracts and
|
|
|
28
28
|
[SQLModel](https://sqlmodel.tiangolo.com/) table models, plus optional CP1
|
|
29
29
|
control-plane reference stores. Install when you need `contract_to_sqlmodel`
|
|
30
30
|
helpers or SQLModel-backed definition/submission stores for local CP1 demos.
|
|
31
|
-
Package version is **0.
|
|
31
|
+
Package version is **0.56.0** — pin with core.
|
|
32
32
|
|
|
33
33
|
## Install
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
pip install 'etlantic-sqlmodel==0.
|
|
37
|
-
# pip install 'etlantic==0.
|
|
36
|
+
pip install 'etlantic-sqlmodel==0.56.0'
|
|
37
|
+
# pip install 'etlantic==0.56.0'
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Schema bridge
|
|
@@ -96,6 +96,6 @@ for isolation queries. Use separate engines/schemas per tenant for
|
|
|
96
96
|
|
|
97
97
|
## Links
|
|
98
98
|
|
|
99
|
-
[Optional packages](https://etlantic.readthedocs.io/en/v0.
|
|
99
|
+
[Optional packages](https://etlantic.readthedocs.io/en/v0.56.0/10_REFERENCE/OPTIONAL_PACKAGES/) ·
|
|
100
100
|
[Source](https://github.com/eddiethedean/etlantic/tree/main/packages/etlantic-sqlmodel) ·
|
|
101
101
|
[Issues](https://github.com/eddiethedean/etlantic/issues)
|
|
@@ -4,13 +4,13 @@ Optional bridge between ETLantic `Data` contracts and
|
|
|
4
4
|
[SQLModel](https://sqlmodel.tiangolo.com/) table models, plus optional CP1
|
|
5
5
|
control-plane reference stores. Install when you need `contract_to_sqlmodel`
|
|
6
6
|
helpers or SQLModel-backed definition/submission stores for local CP1 demos.
|
|
7
|
-
Package version is **0.
|
|
7
|
+
Package version is **0.56.0** — pin with core.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pip install 'etlantic-sqlmodel==0.
|
|
13
|
-
# pip install 'etlantic==0.
|
|
12
|
+
pip install 'etlantic-sqlmodel==0.56.0'
|
|
13
|
+
# pip install 'etlantic==0.56.0'
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Schema bridge
|
|
@@ -72,6 +72,6 @@ for isolation queries. Use separate engines/schemas per tenant for
|
|
|
72
72
|
|
|
73
73
|
## Links
|
|
74
74
|
|
|
75
|
-
[Optional packages](https://etlantic.readthedocs.io/en/v0.
|
|
75
|
+
[Optional packages](https://etlantic.readthedocs.io/en/v0.56.0/10_REFERENCE/OPTIONAL_PACKAGES/) ·
|
|
76
76
|
[Source](https://github.com/eddiethedean/etlantic/tree/main/packages/etlantic-sqlmodel) ·
|
|
77
77
|
[Issues](https://github.com/eddiethedean/etlantic/issues)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "etlantic-sqlmodel"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.56.0"
|
|
4
4
|
description = "ContractModel ↔ SQLModel bridge for ETLantic."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -17,7 +17,7 @@ classifiers = [
|
|
|
17
17
|
"Typing :: Typed",
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
20
|
-
"etlantic>=0.
|
|
20
|
+
"etlantic>=0.56.0,<0.57",
|
|
21
21
|
"sqlmodel>=0.0.22,<1",
|
|
22
22
|
]
|
|
23
23
|
|
|
@@ -25,6 +25,9 @@ from etlantic_sqlmodel.control_plane.durable_stores import (
|
|
|
25
25
|
SQLModelDurableWorkStore,
|
|
26
26
|
create_durable_tables,
|
|
27
27
|
)
|
|
28
|
+
from etlantic_sqlmodel.control_plane.input_resource_stores import (
|
|
29
|
+
SqlModelInputResourceStore,
|
|
30
|
+
)
|
|
28
31
|
from etlantic_sqlmodel.control_plane.models import (
|
|
29
32
|
AliasRow,
|
|
30
33
|
Cp4GovernanceSnapshotRow,
|
|
@@ -37,6 +40,7 @@ from etlantic_sqlmodel.control_plane.models import (
|
|
|
37
40
|
LogicalIdentityRow,
|
|
38
41
|
PromotionRow,
|
|
39
42
|
RevisionRow,
|
|
43
|
+
RunReportRow,
|
|
40
44
|
ScheduleSnapshotRow,
|
|
41
45
|
SecurityDomainRow,
|
|
42
46
|
SubmissionRow,
|
|
@@ -61,6 +65,11 @@ from etlantic_sqlmodel.control_plane.registry_stores import (
|
|
|
61
65
|
SqlModelWorkspaceDirectory,
|
|
62
66
|
create_registry_tables,
|
|
63
67
|
)
|
|
68
|
+
from etlantic_sqlmodel.control_plane.report_stores import (
|
|
69
|
+
SqlModelRunReportStore,
|
|
70
|
+
SqlModelRunReportStoreProvider,
|
|
71
|
+
create_run_report_tables,
|
|
72
|
+
)
|
|
64
73
|
from etlantic_sqlmodel.control_plane.schedule_stores import (
|
|
65
74
|
SCHEDULE_TABLES,
|
|
66
75
|
SQLModelScheduleStore,
|
|
@@ -97,6 +106,7 @@ __all__ = [
|
|
|
97
106
|
"LogicalIdentityRow",
|
|
98
107
|
"PromotionRow",
|
|
99
108
|
"RevisionRow",
|
|
109
|
+
"RunReportRow",
|
|
100
110
|
"SQLModelApprovalStore",
|
|
101
111
|
"SQLModelAttestationStore",
|
|
102
112
|
"SQLModelAuditEvidenceStore",
|
|
@@ -111,8 +121,11 @@ __all__ = [
|
|
|
111
121
|
"ScheduleSnapshotRow",
|
|
112
122
|
"SecurityDomainRow",
|
|
113
123
|
"SqlModelEventStore",
|
|
124
|
+
"SqlModelInputResourceStore",
|
|
114
125
|
"SqlModelRegistryProvider",
|
|
115
126
|
"SqlModelRevisionRegistry",
|
|
127
|
+
"SqlModelRunReportStore",
|
|
128
|
+
"SqlModelRunReportStoreProvider",
|
|
116
129
|
"SqlModelTenantDirectory",
|
|
117
130
|
"SqlModelWorkspaceDirectory",
|
|
118
131
|
"SubmissionRow",
|
|
@@ -124,6 +137,7 @@ __all__ = [
|
|
|
124
137
|
"create_cp4_tables",
|
|
125
138
|
"create_durable_tables",
|
|
126
139
|
"create_registry_tables",
|
|
140
|
+
"create_run_report_tables",
|
|
127
141
|
"create_schedule_tables",
|
|
128
142
|
"create_sqlite_engine",
|
|
129
143
|
"dump_registry_sqlite",
|
|
@@ -5,9 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
from collections.abc import Callable, Mapping, Sequence
|
|
7
7
|
from datetime import UTC, datetime
|
|
8
|
-
from typing import Any, TypeVar
|
|
8
|
+
from typing import Any, TypeVar, cast
|
|
9
9
|
|
|
10
|
+
from sqlalchemy import Table, update
|
|
10
11
|
from sqlalchemy.engine import Engine
|
|
12
|
+
from sqlalchemy.exc import IntegrityError
|
|
11
13
|
|
|
12
14
|
from etlantic.control_plane.approval_memory import MemoryApprovalStore
|
|
13
15
|
from etlantic.control_plane.approval_models import (
|
|
@@ -36,7 +38,12 @@ from etlantic.control_plane.objective_models import (
|
|
|
36
38
|
)
|
|
37
39
|
from etlantic.control_plane.policy_memory import MemoryPolicyProvider
|
|
38
40
|
from etlantic.control_plane.quota_memory import MemoryQuotaProvider
|
|
39
|
-
from etlantic.control_plane.quota_models import
|
|
41
|
+
from etlantic.control_plane.quota_models import (
|
|
42
|
+
QuotaDecision,
|
|
43
|
+
QuotaEffect,
|
|
44
|
+
QuotaResource,
|
|
45
|
+
QuotaState,
|
|
46
|
+
)
|
|
40
47
|
from etlantic_sqlmodel.control_plane.models import Cp4GovernanceSnapshotRow
|
|
41
48
|
from etlantic_sqlmodel.control_plane.session import session_scope
|
|
42
49
|
from sqlmodel import Session, SQLModel, select
|
|
@@ -112,6 +119,7 @@ class _SnapshotBackedStore:
|
|
|
112
119
|
|
|
113
120
|
def _txn(self, fn: Callable[[Any], T]) -> T:
|
|
114
121
|
with session_scope(self.engine) as session:
|
|
122
|
+
self._ensure_snapshot_row(session)
|
|
115
123
|
mem, version = self._read(session, for_update=True)
|
|
116
124
|
result = fn(mem)
|
|
117
125
|
self._write(session, mem, expected_version=version)
|
|
@@ -137,33 +145,65 @@ class _SnapshotBackedStore:
|
|
|
137
145
|
row.payload_version or 0
|
|
138
146
|
)
|
|
139
147
|
|
|
140
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
def _ensure_snapshot_row(self, session: Session) -> None:
|
|
149
|
+
"""Create a version-zero lock row without racing on first use.
|
|
150
|
+
|
|
151
|
+
``SELECT FOR UPDATE`` cannot lock a row that does not exist. When two
|
|
152
|
+
processes perform the first governance write at once, both would
|
|
153
|
+
otherwise observe absence and race to insert the unique
|
|
154
|
+
``(store_id, kind)`` row. Insert under a savepoint so the loser can
|
|
155
|
+
recover from that uniqueness race and continue by locking the winner's
|
|
156
|
+
committed row.
|
|
157
|
+
"""
|
|
158
|
+
statement = (
|
|
143
159
|
select(Cp4GovernanceSnapshotRow)
|
|
144
160
|
.where(Cp4GovernanceSnapshotRow.store_id == self.store_id)
|
|
145
161
|
.where(Cp4GovernanceSnapshotRow.kind == self.kind)
|
|
146
|
-
|
|
147
|
-
).first()
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
session.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
)
|
|
163
|
+
if session.exec(statement).first() is not None:
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
with session.begin_nested():
|
|
168
|
+
session.add(
|
|
169
|
+
Cp4GovernanceSnapshotRow(
|
|
170
|
+
store_id=self.store_id,
|
|
171
|
+
kind=self.kind,
|
|
172
|
+
payload_json=json.dumps(
|
|
173
|
+
self._dump(self._empty()), sort_keys=True, default=str
|
|
174
|
+
),
|
|
175
|
+
payload_version=0,
|
|
176
|
+
updated_at=_utcnow_iso(),
|
|
177
|
+
)
|
|
158
178
|
)
|
|
179
|
+
session.flush()
|
|
180
|
+
except IntegrityError:
|
|
181
|
+
# The conflict is recoverable only when another transaction created
|
|
182
|
+
# the exact scoped snapshot row. Other integrity errors still fail
|
|
183
|
+
# closed.
|
|
184
|
+
if session.exec(statement).first() is None:
|
|
185
|
+
raise
|
|
186
|
+
|
|
187
|
+
def _write(self, session: Session, store: Any, *, expected_version: int) -> None:
|
|
188
|
+
payload = json.dumps(self._dump(store), sort_keys=True, default=str)
|
|
189
|
+
# Compare the version in the UPDATE itself. SQLite ignores FOR UPDATE,
|
|
190
|
+
# so an ORM read followed by a plain write can overwrite another
|
|
191
|
+
# transaction's quota claims and release the wrong reservation.
|
|
192
|
+
# SQLModel adds this table attribute when the model class is mapped.
|
|
193
|
+
table = cast(Table, Cp4GovernanceSnapshotRow.__table__) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
194
|
+
result = session.connection().execute(
|
|
195
|
+
update(table)
|
|
196
|
+
.where(table.c.store_id == self.store_id)
|
|
197
|
+
.where(table.c.kind == self.kind)
|
|
198
|
+
.where(table.c.payload_version == expected_version)
|
|
199
|
+
.values(
|
|
200
|
+
payload_json=payload,
|
|
201
|
+
payload_version=expected_version + 1,
|
|
202
|
+
updated_at=_utcnow_iso(),
|
|
159
203
|
)
|
|
160
|
-
|
|
161
|
-
if
|
|
204
|
+
)
|
|
205
|
+
if result.rowcount != 1:
|
|
162
206
|
raise ControlPlaneError.conflict("CP4 snapshot conflict")
|
|
163
|
-
row.payload_json = payload
|
|
164
|
-
row.payload_version = expected_version + 1
|
|
165
|
-
row.updated_at = _utcnow_iso()
|
|
166
|
-
session.add(row)
|
|
167
207
|
|
|
168
208
|
|
|
169
209
|
class SQLModelAuditEvidenceStore(_SnapshotBackedStore):
|
|
@@ -321,6 +361,22 @@ class SQLModelQuotaProvider(_SnapshotBackedStore):
|
|
|
321
361
|
"default_limits": dict(mem.default_limits),
|
|
322
362
|
"weights": {f"{t}|{w}": wt for (t, w), wt in mem.weights.items()},
|
|
323
363
|
"states": {f"{t}|{w}": s.to_dict() for (t, w), s in mem._states.items()},
|
|
364
|
+
"admissions": {
|
|
365
|
+
f"{t}|{w}|{key}": {
|
|
366
|
+
"resource": resource,
|
|
367
|
+
"units": units,
|
|
368
|
+
"decision": decision.to_dict(),
|
|
369
|
+
}
|
|
370
|
+
for (t, w, key), (resource, units, decision) in mem._admissions.items()
|
|
371
|
+
},
|
|
372
|
+
"releases": {
|
|
373
|
+
f"{t}|{w}|{key}": {"resource": resource, "units": units}
|
|
374
|
+
for (t, w, key), (resource, units) in mem._releases.items() # pyright: ignore[reportPrivateUsage]
|
|
375
|
+
},
|
|
376
|
+
"claims": {
|
|
377
|
+
f"{t}|{w}|{key}": sorted(claims)
|
|
378
|
+
for (t, w, key), claims in mem._claims.items() # pyright: ignore[reportPrivateUsage]
|
|
379
|
+
},
|
|
324
380
|
"rr_cursor": int(mem._rr_cursor),
|
|
325
381
|
"shared_pressure": bool(getattr(mem, "shared_pressure", False)),
|
|
326
382
|
}
|
|
@@ -348,7 +404,31 @@ class SQLModelQuotaProvider(_SnapshotBackedStore):
|
|
|
348
404
|
if raw.get("updated_at")
|
|
349
405
|
else datetime.now(UTC),
|
|
350
406
|
)
|
|
407
|
+
for key, raw in dict(payload.get("admissions") or {}).items():
|
|
408
|
+
t, w, idem = str(key).split("|", 2)
|
|
409
|
+
decision = raw["decision"]
|
|
410
|
+
mem._admissions[(t, w, idem)] = (
|
|
411
|
+
cast(QuotaResource, str(raw["resource"])),
|
|
412
|
+
int(raw["units"]),
|
|
413
|
+
QuotaDecision(
|
|
414
|
+
effect=cast(QuotaEffect, str(decision["effect"])),
|
|
415
|
+
resource=cast(QuotaResource, str(decision["resource"])),
|
|
416
|
+
limit=int(decision["limit"]),
|
|
417
|
+
used=int(decision["used"]),
|
|
418
|
+
reason=str(decision["reason"]),
|
|
419
|
+
metadata=dict(decision.get("metadata") or {}),
|
|
420
|
+
),
|
|
421
|
+
)
|
|
422
|
+
for key, raw in dict(payload.get("releases") or {}).items():
|
|
423
|
+
t, w, idem = str(key).split("|", 2)
|
|
424
|
+
mem._releases[(t, w, idem)] = ( # pyright: ignore[reportPrivateUsage]
|
|
425
|
+
cast(QuotaResource, str(raw["resource"])),
|
|
426
|
+
int(raw["units"]),
|
|
427
|
+
)
|
|
351
428
|
mem._rr_cursor = int(payload.get("rr_cursor") or 0)
|
|
429
|
+
for key, claims in dict(payload.get("claims") or {}).items():
|
|
430
|
+
t, w, idem = str(key).split("|", 2)
|
|
431
|
+
mem._claims[(t, w, idem)] = set(claims) # pyright: ignore[reportPrivateUsage]
|
|
352
432
|
mem.shared_pressure = bool(payload.get("shared_pressure"))
|
|
353
433
|
return mem
|
|
354
434
|
|