|
12 | 12 | from typing import TypeVar |
13 | 13 | from typing import Union |
14 | 14 |
|
| 15 | +try: |
| 16 | + import msgpack |
| 17 | + _has_msgpack = True |
| 18 | +except ImportError: |
| 19 | + msgpack = None # type: ignore[assignment] |
| 20 | + _has_msgpack = False |
| 21 | + |
15 | 22 | try: |
16 | 23 | from typing import TypeVarTuple # type: ignore |
17 | 24 | from typing import Unpack # type: ignore |
@@ -153,10 +160,86 @@ def json_or_null_loads(v: Optional[str], **kwargs: Any) -> Optional[Any]: |
153 | 160 | ] |
154 | 161 |
|
155 | 162 |
|
| 163 | +def msgpack_or_null_dumps(v: Optional[Any], **kwargs: Any) -> Optional[bytes]: |
| 164 | + """ |
| 165 | + Serialize a Python object to MessagePack bytes or None. |
| 166 | +
|
| 167 | + Parameters |
| 168 | + ---------- |
| 169 | + v : Optional[Any] |
| 170 | + The Python object to serialize. If None or empty, the function returns None. |
| 171 | + **kwargs : Any |
| 172 | + Additional keyword arguments to pass to `msgpack.packb`. |
| 173 | +
|
| 174 | + Returns |
| 175 | + ------- |
| 176 | + Optional[bytes] |
| 177 | + The MessagePack bytes representation of the input object, |
| 178 | + or None if the input is None or empty. |
| 179 | +
|
| 180 | + Raises |
| 181 | + ------ |
| 182 | + ImportError |
| 183 | + If msgpack is not installed. |
| 184 | +
|
| 185 | + """ |
| 186 | + if not _has_msgpack: |
| 187 | + raise ImportError('msgpack is required for MessagePack serialization') |
| 188 | + if not v: |
| 189 | + return None |
| 190 | + return msgpack.packb(v, datetime=True, **kwargs) |
| 191 | + |
| 192 | + |
| 193 | +# Force numpy dtype to 'object' to avoid issues with |
| 194 | +# numpy trying to infer the dtype and creating multidimensional arrays |
| 195 | +# instead of an array of Python objects. |
| 196 | +@output_type('object') |
| 197 | +def msgpack_or_null_loads(v: Optional[bytes], **kwargs: Any) -> Optional[Any]: |
| 198 | + """ |
| 199 | + Deserialize MessagePack bytes to a Python object or None. |
| 200 | +
|
| 201 | + Parameters |
| 202 | + ---------- |
| 203 | + v : Optional[bytes] |
| 204 | + The MessagePack bytes to deserialize. If None or empty, |
| 205 | + the function returns None. |
| 206 | + **kwargs : Any |
| 207 | + Additional keyword arguments to pass to `msgpack.unpackb`. |
| 208 | +
|
| 209 | + Returns |
| 210 | + ------- |
| 211 | + Optional[Any] |
| 212 | + The Python object represented by the MessagePack bytes, |
| 213 | + or None if the input is None or empty. |
| 214 | +
|
| 215 | + Raises |
| 216 | + ------ |
| 217 | + ImportError |
| 218 | + If msgpack is not installed. |
| 219 | +
|
| 220 | + """ |
| 221 | + if not _has_msgpack: |
| 222 | + raise ImportError('msgpack is required for MessagePack deserialization') |
| 223 | + if not v: |
| 224 | + return None |
| 225 | + return msgpack.unpackb(v, raw=False, strict_map_key=False, timestamp=3, **kwargs) |
| 226 | + |
| 227 | + |
| 228 | +MessagePack: TypeAlias = Annotated[ |
| 229 | + Union[Dict[str, Any], List[Any], int, float, str, bool, bytes, None], |
| 230 | + UDFAttrs( |
| 231 | + sql_type=sql_types.BLOB(nullable=False), |
| 232 | + args_transformer=msgpack_or_null_loads, |
| 233 | + returns_transformer=msgpack_or_null_dumps, |
| 234 | + ), |
| 235 | +] |
| 236 | + |
| 237 | + |
156 | 238 | __all__ = [ |
157 | 239 | 'Table', |
158 | 240 | 'Masked', |
159 | 241 | 'JSON', |
| 242 | + 'MessagePack', |
160 | 243 | 'UDFAttrs', |
161 | 244 | 'Transformer', |
162 | 245 | ] |
0 commit comments