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
12 changes: 12 additions & 0 deletions multihash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
Cast,
MHFromBytes,
Multihash,
MultihashReader,
MultihashSet,
MultihashWriter,
NewReader,
NewWriter,
cast,
coerce_code,
decode,
Expand All @@ -30,6 +34,8 @@
is_valid,
is_valid_code,
mh_from_bytes,
new_reader,
new_writer,
sum,
sum_stream,
to_b58_string,
Expand All @@ -46,7 +52,11 @@
"MHFromBytes",
"Multihash",
"MultihashError",
"MultihashReader",
"MultihashSet",
"MultihashWriter",
"NewReader",
"NewWriter",
"ShakeHash",
"TruncationError",
"UnsupportedCodeError",
Expand All @@ -63,6 +73,8 @@
"is_valid",
"is_valid_code",
"mh_from_bytes",
"new_reader",
"new_writer",
"sum",
"sum_stream",
"to_b58_string",
Expand Down
54 changes: 54 additions & 0 deletions multihash/multihash.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions newsfragments/57.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :class:`~multihash.MultihashReader` and :class:`~multihash.MultihashWriter` stream wrappers (``new_reader`` / ``new_writer``) matching go-multihash ``NewReader`` / ``NewWriter``.
26 changes: 26 additions & 0 deletions tests/test_new_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Loading