jam.utils.codec.codable
Base class for types that can encode and decode themselves.
Interface
Codable[T]
class Codable(Generic[T]):
def __init__(self,
codec: Optional[Codec[T]] = None,
enc_sequence: Optional[Callable[[], list[Any]]] = None):
self.codec = codec
self.enc_sequence = enc_sequence
Usage Patterns
Direct Codec
Use a specific codec for encoding/decoding:
class Point(Codable[tuple[int, int]]):
def __init__(self, x: int, y: int):
super().__init__(codec=TupleCodec(IntCodec(), IntCodec()))
self.x = x
self.y = y
def value(self) -> tuple[int, int]:
return (self.x, self.y)
Sequence Encoding
Encode as a sequence of values:
class Point(Codable[tuple[int, int]]):
def __init__(self, x: int, y: int):
super().__init__(enc_sequence=lambda: [self.x, self.y])
self.x = x
self.y = y
Custom Encoding
Implement custom encode/decode logic:
class Point(Codable[tuple[int, int]]):
def encode(self) -> bytes:
return bytes([self.x, self.y])
@classmethod
def decode(cls, buffer: bytes) -> 'Point':
return cls(buffer[0], buffer[1])
Implementation Details
Value Property
The value property provides the raw value for encoding:
@property
def value(self) -> T:
"""Get the value to encode."""
if hasattr(self, 'get_value'):
return self.get_value()
return self._value
Encoding Process
Get value via property
Use codec if provided
Use sequence if provided
Call custom encode()
Decoding Process
Use codec if provided
Use sequence if provided
Call custom decode()
Set value property
Error Handling
Common error cases:
Missing codec/sequence:
if not self.codec and not self.enc_sequence: raise CodecError("No codec or sequence provided")
Invalid sequence:
if not all(isinstance(x, Codable) for x in sequence): raise CodecError("Sequence contains non-Codable values")
Examples
Basic Usage
class Point(Codable[tuple[int, int]]):
def __init__(self, x: int, y: int):
super().__init__(codec=TupleCodec(IntCodec(), IntCodec()))
self.x = x
self.y = y
def value(self) -> tuple[int, int]:
return (self.x, self.y)
point = Point(1, 2)
encoded = point.encode() # -> [01 00 00 00 02 00 00 00]
decoded = Point.decode(encoded) # -> Point(1, 2)
Sequence Example
class Vector(Codable[list[int]]):
def __init__(self, *components: int):
super().__init__(enc_sequence=lambda: list(components))
self.components = components
vec = Vector(1, 2, 3)
encoded = vec.encode() # -> [03 01 02 03]
decoded = Vector.decode(encoded) # -> Vector(1, 2, 3)
API Reference
Base class for types that can encode/decode themselves.
- class jam.utils.codec.codable.Codable(codec: Codec[Any] | None = None)[source]
Bases:
Generic[T]Base class for all codable types.
Can be used in three ways: 1. With a codec: Initialize with codec=some_codec 2. With sequence: Initialize with enc_sequence=lambda: [field1, field2, …] 3. With JSON: Use to_json() and from_json() for JSON serialization
- __init__(codec: Codec[Any] | None = None)[source]
Initialize the Codable.
- Parameters:
codec – Optional codec to use for encoding/decoding
enc_sequence – Optional function that returns sequence of fields to encode
- static decode_from(buffer: bytes | bytearray | memoryview, offset: int = 0) Tuple[Any, int][source]
Decode from buffer. Must be implemented by subclasses or added via decorator.
- Parameters:
buffer – Buffer to decode from
offset – Starting position in buffer
- Returns:
The decoded value
Number of bytes read
- Return type:
Tuple containing