From 40e9d6c28148709706ea036b3cadc69e56952410 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Thu, 21 Mar 2024 15:49:23 -0700 Subject: [PATCH] add doc.get_last_local_change --- rust/src/lib.rs | 11 +++++++++++ src/automerge/_automerge.pyi | 1 + tests/test_basic.py | 28 ++++++++++++++++++++++++++++ tests/test_changes.py | 10 ++++++---- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a4af4e1..4d71a5a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -366,6 +366,17 @@ impl Document { Ok(inner.get_heads()) } + fn get_last_local_change(&self) -> PyResult> { + let inner = self + .inner + .read() + .map_err(|e| PyException::new_err(e.to_string()))?; + Ok(inner + .doc + .get_last_local_change() + .map(|c| PyChange(c.to_owned()))) + } + fn object_type(&self, obj_id: PyObjId) -> PyResult { let inner = self .inner diff --git a/src/automerge/_automerge.pyi b/src/automerge/_automerge.pyi index 9342a02..a3a31d5 100644 --- a/src/automerge/_automerge.pyi +++ b/src/automerge/_automerge.pyi @@ -21,6 +21,7 @@ class Document: def receive_sync_message(self, state: SyncState, msg: Message) -> None: ... def get_heads(self) -> list[bytes]: ... + def get_last_local_change(self) -> Optional[Change]: ... def object_type(self, obj_id: bytes) -> ObjType: ... def get_changes(self, have_deps: list[bytes]) -> list[Change]: ... def get(self, obj_id: bytes, prop: str | int, heads: Optional[list[bytes]] = None) -> Optional[tuple[Value, bytes]]: ... diff --git a/tests/test_basic.py b/tests/test_basic.py index 1914d82..136d8d2 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -73,3 +73,31 @@ def test_diff() -> None: patch = doc.diff([], doc.get_heads()) assert len(patch) == 4 +def test_text_heads() -> None: + doc1 = Document(actor_id=b'A') + + with doc1.transaction() as tx: + text_id = tx.put_object(ROOT, "text", ObjType.Text) + tx.insert(text_id, 0, ScalarType.Str, "h") + + doc2 = doc1.fork() + doc2.set_actor(b'B') + + with doc1.transaction() as tx: + tx.insert(text_id, 1, ScalarType.Str, "i") + + with doc2.transaction() as tx: + tx.insert(text_id, 1, ScalarType.Str, "o") + + a_change = doc1.get_last_local_change() + assert a_change is not None + b_change = doc2.get_last_local_change() + assert b_change is not None + + doc1.merge(doc2) + heads = doc1.get_heads() + assert len(heads) == 2 + + assert doc1.text(text_id, [a_change.hash]) == 'hi' + assert doc1.text(text_id, [b_change.hash]) == 'ho' + assert doc1.text(text_id, [a_change.hash, b_change.hash]) == 'hoi' diff --git a/tests/test_changes.py b/tests/test_changes.py index a66c811..cd26209 100644 --- a/tests/test_changes.py +++ b/tests/test_changes.py @@ -1,6 +1,6 @@ from datetime import datetime -from typing import List -from automerge.core import Document, ROOT, ScalarType, ObjType, extract +from typing import List, Optional, Tuple +from automerge.core import Document, ROOT, ScalarType, ObjType def test_get_changes() -> None: doc = Document() @@ -68,6 +68,8 @@ def test_multi_author_changes() -> None: docB.merge(docA) docA.merge(docB) + assert len(docA.get_heads()) == 2 + # docA and docB are the same now, so pick one arbitrarily to read changes # out of for history linearization doc = docA @@ -76,8 +78,8 @@ def test_multi_author_changes() -> None: assert changes[0].actor_id == b'A' assert changes[1].actor_id == b'B' - last_actor: bytes | None = None - snapshots: List[tuple[str, bytes | None]] = [] + last_actor: Optional[bytes] = None + snapshots: List[Tuple[str, Optional[bytes]]] = [] seen_heads: List[bytes] = [] # Go through each of the changes, saving when the author changes. for change in changes: