Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions multihash/multihash.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,49 @@ def All(self) -> list[Multihash]:
"""
return list(self._set)

def Visit(self, mh: Multihash) -> bool:
"""Add mh if not present. Return True if it was added (Go-style API).

Args:
mh: Multihash object to visit/add

Returns:
True if mh was newly added, False if it was already present

Raises:
TypeError: If mh is not a Multihash object
"""
if not isinstance(mh, Multihash):
raise TypeError(f"MultihashSet can only contain Multihash objects, got {type(mh)}")
if mh in self._set:
return False
self._set.add(mh)
return True

def visit(self, mh: Multihash) -> bool:
"""Add mh if not present. Return True if it was added (Python-style API)."""
return self.Visit(mh)

def ForEach(self, func):
"""Call ``func(mh)`` for each Multihash. Stop and return the error if raised.

Args:
func: Callable invoked with each Multihash in the set

Returns:
``None`` on success, or the exception instance if ``func`` raises
"""
for mh in self._set:
try:
func(mh)
except Exception as e:
return e
return None

def for_each(self, func):
"""Call ``func(mh)`` for each Multihash (Python-style API)."""
return self.ForEach(func)

def __len__(self) -> int:
"""Return the number of Multihash objects in the set.

Expand Down
1 change: 1 addition & 0 deletions newsfragments/51.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``Visit``/``visit`` and ``ForEach``/``for_each`` methods to :class:`~multihash.MultihashSet` for go-multihash parity.
21 changes: 21 additions & 0 deletions tests/test_new_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,27 @@ def test_multihash_set_go_api(self):
mh_set.Remove(mh1)
assert mh_set.Has(mh1) is False

def test_visit(self):
"""Test Visit/visit add-if-absent semantics."""
mh_set = MultihashSet()
mh1 = sum(b"file1", Func.sha2_256)
assert mh_set.visit(mh1) is True
assert mh_set.Visit(mh1) is False
assert len(mh_set) == 1

def test_for_each(self):
"""Test ForEach/for_each iteration and error propagation."""
mh1 = sum(b"a", Func.sha2_256)
mh2 = sum(b"b", Func.sha2_256)
mh_set = MultihashSet([mh1, mh2])
collected = []
assert mh_set.for_each(lambda mh: collected.append(mh)) is None
assert len(collected) == 2

err = mh_set.ForEach(lambda mh: (_ for _ in ()).throw(ValueError("stop")))
assert isinstance(err, ValueError)
assert str(err) == "stop"

def test_multihash_set_clear(self):
"""Test clearing MultihashSet."""
mh1 = sum(b"file1", Func.sha2_256)
Expand Down
Loading