From 624a307e792c48e32c24958b3f635a06fe905f3b Mon Sep 17 00:00:00 2001 From: acul71 Date: Fri, 2 Oct 2026 04:42:33 -0400 Subject: [PATCH] Add MultihashReader and MultihashWriter stream wrappers. Co-authored-by: Cursor --- multihash/__init__.py | 12 ++++++++ multihash/multihash.py | 54 ++++++++++++++++++++++++++++++++++++ newsfragments/57.feature.rst | 1 + tests/test_new_features.py | 26 +++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 newsfragments/57.feature.rst diff --git a/multihash/__init__.py b/multihash/__init__.py index 6b5b078..344b88d 100644 --- a/multihash/__init__.py +++ b/multihash/__init__.py @@ -16,7 +16,11 @@ Cast, MHFromBytes, Multihash, + MultihashReader, MultihashSet, + MultihashWriter, + NewReader, + NewWriter, cast, coerce_code, decode, @@ -30,6 +34,8 @@ is_valid, is_valid_code, mh_from_bytes, + new_reader, + new_writer, sum, sum_stream, to_b58_string, @@ -46,7 +52,11 @@ "MHFromBytes", "Multihash", "MultihashError", + "MultihashReader", "MultihashSet", + "MultihashWriter", + "NewReader", + "NewWriter", "ShakeHash", "TruncationError", "UnsupportedCodeError", @@ -63,6 +73,8 @@ "is_valid", "is_valid_code", "mh_from_bytes", + "new_reader", + "new_writer", "sum", "sum_stream", "to_b58_string", diff --git a/multihash/multihash.py b/multihash/multihash.py index f067bc0..8d04f98 100644 --- a/multihash/multihash.py +++ b/multihash/multihash.py @@ -301,6 +301,60 @@ def write(self, stream: BinaryIO) -> int: raise OSError(f"Failed to write multihash to stream: {e}") from e +class MultihashReader: + """Wraps a binary stream with ReadMultihash capability (go-multihash NewReader).""" + + def __init__(self, stream: BinaryIO): + self._stream = stream + + def read(self, size: int = -1) -> bytes: + """Read raw bytes from the underlying stream.""" + return self._stream.read(size) + + def ReadMultihash(self) -> Multihash: + """Read one multihash from the stream (Go-style API).""" + return Multihash.read(self._stream) + + def read_multihash(self) -> Multihash: + """Read one multihash from the stream (Python-style API).""" + return self.ReadMultihash() + + +class MultihashWriter: + """Wraps a binary stream with WriteMultihash capability (go-multihash NewWriter).""" + + def __init__(self, stream: BinaryIO): + self._stream = stream + + def write(self, buf: bytes) -> int: + """Write raw bytes to the underlying stream.""" + return self._stream.write(buf) + + def WriteMultihash(self, mh: Multihash) -> int: + """Write one multihash to the stream (Go-style API).""" + if not isinstance(mh, Multihash): + raise TypeError(f"expected Multihash, got {type(mh)}") + return mh.write(self._stream) + + def write_multihash(self, mh: Multihash) -> int: + """Write one multihash to the stream (Python-style API).""" + return self.WriteMultihash(mh) + + +def new_reader(stream: BinaryIO) -> MultihashReader: + """Create a :class:`MultihashReader` wrapping ``stream``.""" + return MultihashReader(stream) + + +def new_writer(stream: BinaryIO) -> MultihashWriter: + """Create a :class:`MultihashWriter` wrapping ``stream``.""" + return MultihashWriter(stream) + + +NewReader = new_reader +NewWriter = new_writer + + class MultihashSet: """A specialized collection for managing unique Multihash values. diff --git a/newsfragments/57.feature.rst b/newsfragments/57.feature.rst new file mode 100644 index 0000000..e8b7bb4 --- /dev/null +++ b/newsfragments/57.feature.rst @@ -0,0 +1 @@ +Add :class:`~multihash.MultihashReader` and :class:`~multihash.MultihashWriter` stream wrappers (``new_reader`` / ``new_writer``) matching go-multihash ``NewReader`` / ``NewWriter``. diff --git a/tests/test_new_features.py b/tests/test_new_features.py index 1b6da59..c623684 100644 --- a/tests/test_new_features.py +++ b/tests/test_new_features.py @@ -961,3 +961,29 @@ def test_go_aliases(self): encoded = mh.encode() assert Cast(encoded) == cast(encoded) assert MHFromBytes(encoded) == mh_from_bytes(encoded) + + +class TestMultihashReaderWriterTestCase: + """Tests for MultihashReader / MultihashWriter wrappers.""" + + def test_writer_reader_roundtrip(self): + from multihash import new_reader, new_writer + + mh1 = sum(b"one", Func.sha2_256) + mh2 = sum(b"two", Func.sha2_256) + stream = BytesIO() + writer = new_writer(stream) + writer.write_multihash(mh1) + writer.WriteMultihash(mh2) + + stream.seek(0) + reader = new_reader(stream) + assert reader.read_multihash() == mh1 + assert reader.ReadMultihash() == mh2 + + def test_new_reader_writer_aliases(self): + from multihash import NewReader, NewWriter + + stream = BytesIO() + assert type(NewWriter(stream)).__name__ == "MultihashWriter" + assert type(NewReader(stream)).__name__ == "MultihashReader"