pamoja.codec
Idiomatic codec facade.
Python already has json, so this facade takes and returns ordinary values
and does the encoding itself, leaving callers to think in documents rather than
buffers. The conversion and the packing happen in the Rust core.
1"""Idiomatic codec facade. 2 3Python already has :mod:`json`, so this facade takes and returns ordinary values 4and does the encoding itself, leaving callers to think in documents rather than 5buffers. The conversion and the packing happen in the Rust core. 6""" 7 8from __future__ import annotations 9 10import json 11from typing import Any, Sequence 12 13from pamoja._native import Quantizer as _NativeQuantizer 14from pamoja._native import cbor_to_json_bytes as _cbor_to_json_bytes 15from pamoja._native import decode_delta_samples as _decode_delta_samples 16from pamoja._native import encode_delta_samples as _encode_delta_samples 17from pamoja._native import json_to_cbor_bytes as _json_to_cbor_bytes 18 19__all__ = ["Quantizer", "from_cbor", "pack_samples", "to_cbor", "unpack_samples"] 20 21 22def to_cbor(value: Any) -> bytes: 23 """Encode a value as CBOR, typically much smaller than its JSON form. 24 25 :param value: Any JSON-serializable value, or the raw bytes of a JSON 26 document. 27 :returns: The CBOR encoding. 28 :raises ValueError: If the value holds a NaN or an infinity, which JSON 29 has no way to write. 30 :raises TypeError: If the value holds something else JSON cannot write, 31 such as a set. 32 :raises PamojaError: If the bytes given are not a valid JSON document. 33 """ 34 if isinstance(value, (bytes, bytearray, memoryview)): 35 document = bytes(value) 36 else: 37 document = json.dumps(value, allow_nan=False).encode("utf-8") 38 return _json_to_cbor_bytes(document) 39 40 41def from_cbor(cbor: bytes) -> Any: 42 """Decode a CBOR document back into an ordinary Python value. 43 44 :param cbor: The CBOR document to decode. 45 :returns: The decoded value. 46 :raises PamojaError: If the document is malformed, or holds a construct with 47 no JSON equivalent such as a non-string map key. 48 """ 49 return json.loads(_cbor_to_json_bytes(bytes(cbor)).decode("utf-8")) 50 51 52def pack_samples(samples: Sequence[int]) -> bytes: 53 """Delta-encode a series of integer samples into a compact buffer. 54 55 :param samples: The samples, in order. 56 :returns: The packed encoding, far smaller than the samples for a 57 slow-moving series. 58 :raises TypeError: If a sample is not an ``int``. 59 :raises OverflowError: If a sample does not fit in 64 bits. 60 """ 61 return _encode_delta_samples(list(samples)) 62 63 64def unpack_samples(data: bytes) -> list[int]: 65 """Unpack a buffer produced by :func:`pack_samples`. 66 67 :param data: The packed encoding. 68 :returns: The samples, in order. 69 :raises PamojaError: If the buffer is malformed or carries bytes after its 70 last sample. 71 """ 72 return _decode_delta_samples(bytes(data)) 73 74 75class Quantizer: 76 """Packs float readings to a fixed precision, for a link charging per byte. 77 78 Example:: 79 80 quantizer = Quantizer(100) # keep two decimal places 81 packed = quantizer.encode([20.0, 20.1, 20.2]) 82 quantizer.decode(packed) # [20.0, 20.1, 20.2], to within 0.01 83 """ 84 85 __slots__ = ("_native",) 86 87 def __init__(self, scale: float) -> None: 88 """Create a quantizer at the given precision. 89 90 :param scale: The multiplier applied before rounding; ``100`` keeps two 91 decimal places. Must be positive and finite. 92 :raises ValueError: If the scale is not positive and finite. 93 """ 94 self._native = _NativeQuantizer(scale) 95 96 def encode(self, readings: Sequence[float]) -> bytes: 97 """Quantize and pack a batch of readings. 98 99 :param readings: The readings, in order. 100 :returns: The packed encoding. 101 :raises PamojaError: If a reading is not a number, is infinite, or is too 102 large for the scale; the format has no way to carry a missing reading. 103 """ 104 return self._native.encode(list(readings)) 105 106 def decode(self, data: bytes) -> list[float]: 107 """Unpack a batch, to within this quantizer's precision. 108 109 :param data: The encoding produced by :meth:`encode` at the same scale. 110 :returns: The readings, in order. 111 :raises PamojaError: If the buffer is malformed or carries bytes after its 112 last reading. 113 """ 114 return self._native.decode(bytes(data))
class
Quantizer:
76class Quantizer: 77 """Packs float readings to a fixed precision, for a link charging per byte. 78 79 Example:: 80 81 quantizer = Quantizer(100) # keep two decimal places 82 packed = quantizer.encode([20.0, 20.1, 20.2]) 83 quantizer.decode(packed) # [20.0, 20.1, 20.2], to within 0.01 84 """ 85 86 __slots__ = ("_native",) 87 88 def __init__(self, scale: float) -> None: 89 """Create a quantizer at the given precision. 90 91 :param scale: The multiplier applied before rounding; ``100`` keeps two 92 decimal places. Must be positive and finite. 93 :raises ValueError: If the scale is not positive and finite. 94 """ 95 self._native = _NativeQuantizer(scale) 96 97 def encode(self, readings: Sequence[float]) -> bytes: 98 """Quantize and pack a batch of readings. 99 100 :param readings: The readings, in order. 101 :returns: The packed encoding. 102 :raises PamojaError: If a reading is not a number, is infinite, or is too 103 large for the scale; the format has no way to carry a missing reading. 104 """ 105 return self._native.encode(list(readings)) 106 107 def decode(self, data: bytes) -> list[float]: 108 """Unpack a batch, to within this quantizer's precision. 109 110 :param data: The encoding produced by :meth:`encode` at the same scale. 111 :returns: The readings, in order. 112 :raises PamojaError: If the buffer is malformed or carries bytes after its 113 last reading. 114 """ 115 return self._native.decode(bytes(data))
Packs float readings to a fixed precision, for a link charging per byte.
Example::
quantizer = Quantizer(100) # keep two decimal places
packed = quantizer.encode([20.0, 20.1, 20.2])
quantizer.decode(packed) # [20.0, 20.1, 20.2], to within 0.01
Quantizer(scale: float)
88 def __init__(self, scale: float) -> None: 89 """Create a quantizer at the given precision. 90 91 :param scale: The multiplier applied before rounding; ``100`` keeps two 92 decimal places. Must be positive and finite. 93 :raises ValueError: If the scale is not positive and finite. 94 """ 95 self._native = _NativeQuantizer(scale)
Create a quantizer at the given precision.
Parameters
- scale: The multiplier applied before rounding;
100keeps two decimal places. Must be positive and finite.
Raises
- ValueError: If the scale is not positive and finite.
def
encode(self, readings: Sequence[float]) -> bytes:
97 def encode(self, readings: Sequence[float]) -> bytes: 98 """Quantize and pack a batch of readings. 99 100 :param readings: The readings, in order. 101 :returns: The packed encoding. 102 :raises PamojaError: If a reading is not a number, is infinite, or is too 103 large for the scale; the format has no way to carry a missing reading. 104 """ 105 return self._native.encode(list(readings))
Quantize and pack a batch of readings.
Parameters
- readings: The readings, in order. :returns: The packed encoding.
Raises
- PamojaError: If a reading is not a number, is infinite, or is too large for the scale; the format has no way to carry a missing reading.
def
decode(self, data: bytes) -> list[float]:
107 def decode(self, data: bytes) -> list[float]: 108 """Unpack a batch, to within this quantizer's precision. 109 110 :param data: The encoding produced by :meth:`encode` at the same scale. 111 :returns: The readings, in order. 112 :raises PamojaError: If the buffer is malformed or carries bytes after its 113 last reading. 114 """ 115 return self._native.decode(bytes(data))
Unpack a batch, to within this quantizer's precision.
Parameters
- data: The encoding produced by
encode()at the same scale. :returns: The readings, in order.
Raises
- PamojaError: If the buffer is malformed or carries bytes after its last reading.
def
from_cbor(cbor: bytes) -> Any:
42def from_cbor(cbor: bytes) -> Any: 43 """Decode a CBOR document back into an ordinary Python value. 44 45 :param cbor: The CBOR document to decode. 46 :returns: The decoded value. 47 :raises PamojaError: If the document is malformed, or holds a construct with 48 no JSON equivalent such as a non-string map key. 49 """ 50 return json.loads(_cbor_to_json_bytes(bytes(cbor)).decode("utf-8"))
Decode a CBOR document back into an ordinary Python value.
Parameters
- cbor: The CBOR document to decode. :returns: The decoded value.
Raises
- PamojaError: If the document is malformed, or holds a construct with no JSON equivalent such as a non-string map key.
def
pack_samples(samples: Sequence[int]) -> bytes:
53def pack_samples(samples: Sequence[int]) -> bytes: 54 """Delta-encode a series of integer samples into a compact buffer. 55 56 :param samples: The samples, in order. 57 :returns: The packed encoding, far smaller than the samples for a 58 slow-moving series. 59 :raises TypeError: If a sample is not an ``int``. 60 :raises OverflowError: If a sample does not fit in 64 bits. 61 """ 62 return _encode_delta_samples(list(samples))
Delta-encode a series of integer samples into a compact buffer.
Parameters
- samples: The samples, in order. :returns: The packed encoding, far smaller than the samples for a slow-moving series.
Raises
- TypeError: If a sample is not an
int. - OverflowError: If a sample does not fit in 64 bits.
def
to_cbor(value: Any) -> bytes:
23def to_cbor(value: Any) -> bytes: 24 """Encode a value as CBOR, typically much smaller than its JSON form. 25 26 :param value: Any JSON-serializable value, or the raw bytes of a JSON 27 document. 28 :returns: The CBOR encoding. 29 :raises ValueError: If the value holds a NaN or an infinity, which JSON 30 has no way to write. 31 :raises TypeError: If the value holds something else JSON cannot write, 32 such as a set. 33 :raises PamojaError: If the bytes given are not a valid JSON document. 34 """ 35 if isinstance(value, (bytes, bytearray, memoryview)): 36 document = bytes(value) 37 else: 38 document = json.dumps(value, allow_nan=False).encode("utf-8") 39 return _json_to_cbor_bytes(document)
Encode a value as CBOR, typically much smaller than its JSON form.
Parameters
- value: Any JSON-serializable value, or the raw bytes of a JSON document. :returns: The CBOR encoding.
Raises
- ValueError: If the value holds a NaN or an infinity, which JSON has no way to write.
- TypeError: If the value holds something else JSON cannot write, such as a set.
- PamojaError: If the bytes given are not a valid JSON document.
def
unpack_samples(data: bytes) -> list[int]:
65def unpack_samples(data: bytes) -> list[int]: 66 """Unpack a buffer produced by :func:`pack_samples`. 67 68 :param data: The packed encoding. 69 :returns: The samples, in order. 70 :raises PamojaError: If the buffer is malformed or carries bytes after its 71 last sample. 72 """ 73 return _decode_delta_samples(bytes(data))
Unpack a buffer produced by pack_samples().
Parameters
- data: The packed encoding. :returns: The samples, in order.
Raises
- PamojaError: If the buffer is malformed or carries bytes after its last sample.