Coverage for src/blob_dict/blob/__init__.py: 0%
40 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-21 20:53 -0700
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-21 20:53 -0700
1from __future__ import annotations
3from base64 import b64decode, b64encode
4from pathlib import Path
5from typing import Any, Self, override
7from simple_zstd import compress, decompress
10class BytesBlob:
11 def __init__(self, blob: bytes) -> None:
12 super().__init__()
14 self._blob_bytes: bytes = blob
16 def as_blob(
17 self,
18 blob_class: type[BytesBlob],
19 blob_class_args: dict[str, Any] | None = None,
20 ) -> BytesBlob:
21 return blob_class(self._blob_bytes, **(blob_class_args or {}))
23 def as_bytes(self, *, compression: bool = False) -> bytes:
24 return compress(self._blob_bytes) if compression else self._blob_bytes
26 @staticmethod
27 def from_bytes(b: bytes, *, compression: bool = False) -> BytesBlob:
28 return BytesBlob(decompress(b) if compression else b)
30 @staticmethod
31 def from_b64_str(s: str) -> BytesBlob:
32 return BytesBlob(b64decode(s))
34 def as_b64_str(self) -> str:
35 return b64encode(self._blob_bytes).decode("ascii")
37 def __repr__(self) -> str:
38 return f"{self.__class__.__name__}({self.as_bytes().__repr__()})"
40 @classmethod
41 def load(cls: type[Self], f: Path | str) -> Self:
42 return cls(Path(f).expanduser().read_bytes())
44 def dump(self, f: Path | str) -> None:
45 Path(f).expanduser().write_bytes(self.as_bytes())
48class StrBlob(BytesBlob):
49 def __init__(self, blob: bytes | str) -> None:
50 if isinstance(blob, str):
51 blob = blob.encode()
53 super().__init__(blob)
55 def as_str(self) -> str:
56 return self._blob_bytes.decode()
58 def __str__(self) -> str:
59 return self.as_str()
61 @override
62 def __repr__(self) -> str:
63 return f"{self.__class__.__name__}({self.as_str().__repr__()})"