lodum.cbor
1# SPDX-FileCopyrightText: 2025-present Michael R. Bernstein <zopemaven@gmail.com> 2# 3# SPDX-License-Identifier: Apache-2.0 4try: 5 import cbor2 6except ImportError: 7 cbor2 = None # type: ignore 8from typing import Any, Iterator, Type, TypeVar 9 10from .core import Loader, BaseDumper, BaseLoader 11from .exception import DeserializationError 12from .internal import dump, load, DEFAULT_MAX_SIZE 13 14T = TypeVar("T") 15 16# --- Public API --- 17 18 19def dumps(obj: Any) -> bytes: 20 """ 21 Encodes a Python object to CBOR bytes. 22 23 Args: 24 obj: The object to encode. Must be lodum-enabled or a supported type. 25 26 Returns: 27 The CBOR-encoded bytes. 28 29 Raises: 30 ImportError: If cbor2 is not installed. 31 """ 32 if cbor2 is None: 33 raise ImportError( 34 "cbor2 is required for CBOR serialization. Install it with 'pip install lodum[cbor]'." 35 ) 36 dumper = CborDumper() 37 dumped_data = dump(obj, dumper) 38 return cbor2.dumps(dumped_data) 39 40 41def loads(cls: Type[T], cbor_bytes: bytes, max_size: int = DEFAULT_MAX_SIZE) -> T: 42 """ 43 Decodes CBOR bytes into a Python object of the specified type. 44 45 Args: 46 cls: The class to instantiate. 47 cbor_bytes: The CBOR data to decode. 48 max_size: Maximum allowed size of the input bytes. 49 50 Returns: 51 An instance of cls populated with the decoded data. 52 53 Raises: 54 DeserializationError: If the input is invalid or exceeds max_size. 55 ImportError: If cbor2 is not installed. 56 """ 57 if len(cbor_bytes) > max_size: 58 raise DeserializationError( 59 f"Input size ({len(cbor_bytes)}) exceeds maximum allowed ({max_size})" 60 ) 61 62 if cbor2 is None: 63 raise ImportError( 64 "cbor2 is required for CBOR deserialization. Install it with 'pip install lodum[cbor]'." 65 ) 66 try: 67 data = cbor2.loads(cbor_bytes) 68 except Exception as e: 69 raise DeserializationError(f"Failed to parse CBOR: {e}") 70 loader = CborLoader(data) 71 return load(cls, loader) 72 73 74# --- CBOR Dumper Implementation --- 75 76 77class CborDumper(BaseDumper): 78 pass 79 80 81# --- CBOR Loader Implementation --- 82 83 84class CborLoader(BaseLoader): 85 def load_int(self) -> int: 86 if not isinstance(self._data, int): 87 raise DeserializationError(f"Expected int, got {type(self._data).__name__}") 88 return self._data 89 90 def load_str(self) -> str: 91 if not isinstance(self._data, str): 92 raise DeserializationError(f"Expected str, got {type(self._data).__name__}") 93 return self._data 94 95 def load_float(self) -> float: 96 if not isinstance(self._data, (float, int)): 97 raise DeserializationError( 98 f"Expected float, got {type(self._data).__name__}" 99 ) 100 return float(self._data) 101 102 def load_bool(self) -> bool: 103 if not isinstance(self._data, bool): 104 raise DeserializationError( 105 f"Expected bool, got {type(self._data).__name__}" 106 ) 107 return self._data 108 109 def load_list(self) -> Iterator["Loader"]: 110 if not isinstance(self._data, list): 111 raise DeserializationError( 112 f"Expected list, got {type(self._data).__name__}" 113 ) 114 return (CborLoader(item) for item in self._data) 115 116 def load_dict(self) -> Iterator[tuple[str, "Loader"]]: 117 if not isinstance(self._data, dict): 118 raise DeserializationError( 119 f"Expected dict, got {type(self._data).__name__}" 120 ) 121 return ((k, CborLoader(v)) for k, v in self._data.items()) 122 123 def load_bytes_value(self, value: Any) -> bytes: 124 if not isinstance(value, bytes): 125 raise DeserializationError(f"Expected bytes, got {type(value).__name__}") 126 return value
def
dumps(obj: Any) -> bytes:
20def dumps(obj: Any) -> bytes: 21 """ 22 Encodes a Python object to CBOR bytes. 23 24 Args: 25 obj: The object to encode. Must be lodum-enabled or a supported type. 26 27 Returns: 28 The CBOR-encoded bytes. 29 30 Raises: 31 ImportError: If cbor2 is not installed. 32 """ 33 if cbor2 is None: 34 raise ImportError( 35 "cbor2 is required for CBOR serialization. Install it with 'pip install lodum[cbor]'." 36 ) 37 dumper = CborDumper() 38 dumped_data = dump(obj, dumper) 39 return cbor2.dumps(dumped_data)
Encodes a Python object to CBOR bytes.
Args: obj: The object to encode. Must be lodum-enabled or a supported type.
Returns: The CBOR-encoded bytes.
Raises: ImportError: If cbor2 is not installed.
def
loads(cls: Type[~T], cbor_bytes: bytes, max_size: int = 10485760) -> ~T:
42def loads(cls: Type[T], cbor_bytes: bytes, max_size: int = DEFAULT_MAX_SIZE) -> T: 43 """ 44 Decodes CBOR bytes into a Python object of the specified type. 45 46 Args: 47 cls: The class to instantiate. 48 cbor_bytes: The CBOR data to decode. 49 max_size: Maximum allowed size of the input bytes. 50 51 Returns: 52 An instance of cls populated with the decoded data. 53 54 Raises: 55 DeserializationError: If the input is invalid or exceeds max_size. 56 ImportError: If cbor2 is not installed. 57 """ 58 if len(cbor_bytes) > max_size: 59 raise DeserializationError( 60 f"Input size ({len(cbor_bytes)}) exceeds maximum allowed ({max_size})" 61 ) 62 63 if cbor2 is None: 64 raise ImportError( 65 "cbor2 is required for CBOR deserialization. Install it with 'pip install lodum[cbor]'." 66 ) 67 try: 68 data = cbor2.loads(cbor_bytes) 69 except Exception as e: 70 raise DeserializationError(f"Failed to parse CBOR: {e}") 71 loader = CborLoader(data) 72 return load(cls, loader)
Decodes CBOR bytes into a Python object of the specified type.
Args: cls: The class to instantiate. cbor_bytes: The CBOR data to decode. max_size: Maximum allowed size of the input bytes.
Returns: An instance of cls populated with the decoded data.
Raises: DeserializationError: If the input is invalid or exceeds max_size. ImportError: If cbor2 is not installed.
class
CborDumper(lodum.core.BaseDumper):
Base implementation of the Dumper protocol to reduce duplication.
class
CborLoader(lodum.core.BaseLoader):
85class CborLoader(BaseLoader): 86 def load_int(self) -> int: 87 if not isinstance(self._data, int): 88 raise DeserializationError(f"Expected int, got {type(self._data).__name__}") 89 return self._data 90 91 def load_str(self) -> str: 92 if not isinstance(self._data, str): 93 raise DeserializationError(f"Expected str, got {type(self._data).__name__}") 94 return self._data 95 96 def load_float(self) -> float: 97 if not isinstance(self._data, (float, int)): 98 raise DeserializationError( 99 f"Expected float, got {type(self._data).__name__}" 100 ) 101 return float(self._data) 102 103 def load_bool(self) -> bool: 104 if not isinstance(self._data, bool): 105 raise DeserializationError( 106 f"Expected bool, got {type(self._data).__name__}" 107 ) 108 return self._data 109 110 def load_list(self) -> Iterator["Loader"]: 111 if not isinstance(self._data, list): 112 raise DeserializationError( 113 f"Expected list, got {type(self._data).__name__}" 114 ) 115 return (CborLoader(item) for item in self._data) 116 117 def load_dict(self) -> Iterator[tuple[str, "Loader"]]: 118 if not isinstance(self._data, dict): 119 raise DeserializationError( 120 f"Expected dict, got {type(self._data).__name__}" 121 ) 122 return ((k, CborLoader(v)) for k, v in self._data.items()) 123 124 def load_bytes_value(self, value: Any) -> bytes: 125 if not isinstance(value, bytes): 126 raise DeserializationError(f"Expected bytes, got {type(value).__name__}") 127 return value
Base implementation of the Loader protocol to reduce duplication.