Type Python subscription document records

This commit is contained in:
Meta Vens 2026-06-14 04:44:51 +01:00
commit 53c15a8db5
3 changed files with 26 additions and 8 deletions

View file

@ -47,10 +47,13 @@ CultCache, CultNet, and CultMesh. It is evidence, not confetti.
- Python CultNet database subscription changes:
Provides typed `CultNetDatabaseChange` parsing plus
`CultNetDatabaseSubscription.read_next_change(...)` / `iter_changes(...)` for
live raw database subscriptions. Initial snapshot frames can be read as typed
`CultNetRawSnapshotResponse` values through `read_next_snapshot_response(...)`
/ `iter_snapshot_responses(...)`, while raw message reads remain available
for callers that own their own dispatch loop.
live raw database subscriptions. Put/update changes expose their contained
raw document as a typed `CultNetRawDocumentRecord` while preserving the legacy
dict-shaped `document` projection. Initial snapshot frames can be read as
typed `CultNetRawSnapshotResponse` values through
`read_next_snapshot_response(...)` / `iter_snapshot_responses(...)`, while
raw message reads remain available for callers that own their own dispatch
loop.
- Python CultMesh discovery client:
Fetches Verse catalogs and peer exchange responses from the live Python
endpoint over CultNet frames.

View file

@ -3,6 +3,8 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from .snapshot import CultNetRawDocumentRecord
@dataclass(frozen=True)
class CultNetDatabaseChange:
@ -10,6 +12,7 @@ class CultNetDatabaseChange:
subscription_id: str
change_kind: str
document: dict[str, Any] | None = None
raw_document: CultNetRawDocumentRecord | None = None
schema_id: str | None = None
record_key: str | None = None
@ -22,16 +25,18 @@ class CultNetDatabaseChange:
raise ValueError(f"unsupported database changeKind {change_kind!r}")
document = message.get("document")
document_record = dict(document) if isinstance(document, dict) else None
raw_document = CultNetRawDocumentRecord.from_wire(document_record) if document_record is not None else None
schema_id = _optional_string(message.get("schemaId"))
record_key = _optional_string(message.get("recordKey"))
if document_record is not None:
schema_id = schema_id or _optional_string(document_record.get("schemaId"))
record_key = record_key or _optional_string(document_record.get("recordKey"))
if raw_document is not None:
schema_id = schema_id or raw_document.schema_id
record_key = record_key or raw_document.record_key
return cls(
message_id=str(message.get("messageId") or ""),
subscription_id=str(message.get("subscriptionId") or ""),
change_kind=change_kind,
document=document_record,
raw_document=raw_document,
schema_id=schema_id,
record_key=record_key,
)
@ -43,7 +48,9 @@ class CultNetDatabaseChange:
"subscriptionId": self.subscription_id,
"changeKind": self.change_kind,
}
if self.document is not None:
if self.raw_document is not None:
wire["document"] = self.raw_document.to_wire()
elif self.document is not None:
wire["document"] = self.document
if self.schema_id is not None:
wire["schemaId"] = self.schema_id

View file

@ -372,6 +372,8 @@ class CultCacheTests(unittest.TestCase):
})
self.assertEqual(put_change.schema_id, "schema-note")
self.assertEqual(put_change.record_key, "note:1")
self.assertIsInstance(put_change.raw_document, CultNetRawDocumentRecord)
self.assertEqual(put_change.raw_document.payload, b"payload")
self.assertEqual(put_change.to_wire()["document"]["recordKey"], "note:1")
delete_change = CultNetDatabaseChange.from_wire({
@ -383,6 +385,7 @@ class CultCacheTests(unittest.TestCase):
"recordKey": "note:1",
})
self.assertIsNone(delete_change.document)
self.assertIsNone(delete_change.raw_document)
self.assertEqual(delete_change.schema_id, "schema-note")
self.assertEqual(delete_change.to_wire()["recordKey"], "note:1")
@ -635,6 +638,8 @@ class CultCacheTests(unittest.TestCase):
self.assertEqual(snapshot.to_wire()["schemaVersion"], "cultnet.snapshot_response_raw.v0")
self.assertEqual(change.change_kind, "added")
self.assertEqual(change.record_key, "item:sub")
self.assertIsInstance(change.raw_document, CultNetRawDocumentRecord)
self.assertEqual(change.raw_document.record_key, "item:sub")
self.assertEqual(received_versions, [
"cultnet.database_subscribe.v0",
"cultnet.document_put_raw.v0",
@ -1972,8 +1977,11 @@ class CultCacheTests(unittest.TestCase):
self.assertEqual(subscription_change.change_kind, "added")
self.assertEqual(subscription_change.record_key, "note:2")
self.assertEqual(subscription_change.document["recordKey"], "note:2")
self.assertIsInstance(subscription_change.raw_document, CultNetRawDocumentRecord)
self.assertEqual(subscription_change.raw_document.schema_id, "mesh.server_note.v1")
self.assertEqual(subscription_delete.change_kind, "removed")
self.assertEqual(subscription_delete.record_key, "note:2")
self.assertIsNone(subscription_delete.raw_document)
self.assertIsNone(node.database.get(document, "note:2"))
def test_cultmesh_authority_lease_requires_live_matching_lease(self) -> None: