Source code for jam.types.base.dictionary

from typing import (
    Generic,
    Mapping,
    Optional,
    Self,
    Tuple,
    Type,
    TypeVar,
    Union,
    Dict,
    Iterator,
    ItemsView,
    KeysView,
    ValuesView,
    Any,
    Sequence,
)

from jam.utils.codec.codable import Codable
from jam.utils.codec.composite.dictionaries import DictionaryCodec
from jam.utils.json import JsonSerde

K = TypeVar("K", bound=Codable)
V = TypeVar("V", bound=Codable)


[docs] class Dictionary(Generic[K, V], Codable, Mapping[K, V], JsonSerde): """ Dictionary implementation that supports codec operations. A dictionary that maps Codable keys to Codable values, providing both standard dictionary operations and codec functionality for serialization/deserialization. Examples: >>> from jam.types.base.string import String >>> from jam.types.base.integers import Int >>> d = Dictionary({String("key"): Int(42)}) >>> d[String("key")] Int(42) >>> encoded = d.encode() >>> decoded, _ = Dictionary.decode_from(String, Int, encoded) >>> decoded == d True """ key_type: Type[K] value_type: Type[V]
[docs] def __init__(self, initial: Optional[Mapping[K, V]] = None): """ Initialize dictionary. Args: initial: Optional initial key-value pairs Raises: TypeError: If any key or value is not Codable """ if initial is not None: for key, value in initial.items(): if not isinstance(key, Codable) or not isinstance(value, Codable): raise TypeError("Dictionary keys and values must be Codable") super().__init__(codec=DictionaryCodec()) self.value: Dict[K, V] = {} if initial is not None: self.value.update(initial)
[docs] def __getitem__(self, key: K) -> V: """Get value for key.""" return self.value[key]
[docs] def __iter__(self) -> Iterator[K]: """Iterate over keys.""" return iter(self.value)
[docs] def __len__(self) -> int: """Get number of items.""" return len(self.value)
[docs] def __eq__(self, other: object) -> bool: """Compare for equality.""" if not isinstance(other, Dictionary): return False return self.value == other.value
[docs] def __repr__(self) -> str: """Get string representation.""" items = [f"{k!r}: {v!r}" for k, v in self.value.items()] return f"Dictionary({{{', '.join(items)}}})"
[docs] def get(self, key: K, default: Optional[V] = None) -> Optional[V]: """ Get value for key, returning default if key not found. Args: key: Key to look up default: Value to return if key not found Returns: Value for key or default """ return self.value.get(key, default)
[docs] def items(self) -> ItemsView[K, V]: """Get view of (key, value) pairs.""" return self.value.items()
[docs] def keys(self) -> KeysView[K]: """Get view of keys.""" return self.value.keys()
[docs] def values(self) -> ValuesView[V]: """Get view of values.""" return self.value.values()
[docs] def to_json(self) -> Dict[Any, Any]: """Convert to JSON representation.""" return {k.to_json(): v.to_json() for k, v in self.items()}
[docs] @classmethod def from_json(cls: Type[Self], data: Dict[Any, Any] | Sequence[Any]) -> Self: """Create instance from JSON representation.""" if not isinstance(data, dict): raise ValueError("Dictionary: JSON representation must be a dictionary") return cls( { cls.key_type.from_json(k): cls.value_type.from_json(v) for k, v in data.items() } )
[docs] def decodable_dictionary( key_type: Type[K], value_type: Type[V] ) -> Type[Dictionary[K, V]]: def decorator(cls: Type[Dictionary[K, V]]) -> Type[Dictionary[K, V]]: cls.key_type = key_type cls.value_type = value_type @staticmethod def decode_from( buffer: Union[bytes, bytearray, memoryview], offset: int = 0 ) -> Tuple["Dictionary[K, V]", int]: value, size = DictionaryCodec.decode_from( key_type, value_type, buffer, offset ) return Dictionary(value), size cls.decode_from = decode_from return cls return decorator