jsonplus.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. from __future__ import annotations
  2. import dataclasses
  3. import decimal
  4. import importlib
  5. import json
  6. import logging
  7. import pathlib
  8. import pickle
  9. import re
  10. import sys
  11. from collections import deque
  12. from collections.abc import Callable, Sequence
  13. from datetime import date, datetime, time, timedelta, timezone
  14. from enum import Enum
  15. from inspect import isclass
  16. from ipaddress import (
  17. IPv4Address,
  18. IPv4Interface,
  19. IPv4Network,
  20. IPv6Address,
  21. IPv6Interface,
  22. IPv6Network,
  23. )
  24. from typing import Any, Literal
  25. from uuid import UUID
  26. from zoneinfo import ZoneInfo
  27. import ormsgpack
  28. from langchain_core.load.load import Reviver
  29. from langgraph.checkpoint.serde.base import SerializerProtocol
  30. from langgraph.checkpoint.serde.types import SendProtocol
  31. from langgraph.store.base import Item
  32. LC_REVIVER = Reviver()
  33. EMPTY_BYTES = b""
  34. logger = logging.getLogger(__name__)
  35. class JsonPlusSerializer(SerializerProtocol):
  36. """Serializer that uses ormsgpack, with optional fallbacks.
  37. Security note: this serializer is intended for use within the BaseCheckpointSaver
  38. class and called within the Pregel loop. It should not be used on untrusted
  39. python objects. If an attacker can write directly to your checkpoint database,
  40. they may be able to trigger code execution when data is deserialized.
  41. """
  42. def __init__(
  43. self,
  44. *,
  45. pickle_fallback: bool = False,
  46. allowed_json_modules: Sequence[tuple[str, ...]] | Literal[True] | None = None,
  47. __unpack_ext_hook__: Callable[[int, bytes], Any] | None = None,
  48. ) -> None:
  49. self.pickle_fallback = pickle_fallback
  50. self._allowed_modules = (
  51. {mod_and_name for mod_and_name in allowed_json_modules}
  52. if allowed_json_modules and allowed_json_modules is not True
  53. else (allowed_json_modules if allowed_json_modules is True else None)
  54. )
  55. self._unpack_ext_hook = (
  56. __unpack_ext_hook__
  57. if __unpack_ext_hook__ is not None
  58. else _msgpack_ext_hook
  59. )
  60. def _encode_constructor_args(
  61. self,
  62. constructor: Callable | type[Any],
  63. *,
  64. method: None | str | Sequence[None | str] = None,
  65. args: Sequence[Any] | None = None,
  66. kwargs: dict[str, Any] | None = None,
  67. ) -> dict[str, Any]:
  68. out = {
  69. "lc": 2,
  70. "type": "constructor",
  71. "id": (*constructor.__module__.split("."), constructor.__name__),
  72. }
  73. if method is not None:
  74. out["method"] = method
  75. if args is not None:
  76. out["args"] = args
  77. if kwargs is not None:
  78. out["kwargs"] = kwargs
  79. return out
  80. def _reviver(self, value: dict[str, Any]) -> Any:
  81. if self._allowed_modules and (
  82. value.get("lc", None) == 2
  83. and value.get("type", None) == "constructor"
  84. and value.get("id", None) is not None
  85. ):
  86. try:
  87. return self._revive_lc2(value)
  88. except InvalidModuleError as e:
  89. logger.warning(
  90. "Object %s is not in the deserialization allowlist.\n%s",
  91. value["id"],
  92. e.message,
  93. )
  94. return LC_REVIVER(value)
  95. def _revive_lc2(self, value: dict[str, Any]) -> Any:
  96. self._check_allowed_modules(value)
  97. [*module, name] = value["id"]
  98. try:
  99. mod = importlib.import_module(".".join(module))
  100. cls = getattr(mod, name)
  101. method = value.get("method")
  102. if isinstance(method, str):
  103. methods = [getattr(cls, method)]
  104. elif isinstance(method, list):
  105. methods = [cls if m is None else getattr(cls, m) for m in method]
  106. else:
  107. methods = [cls]
  108. args = value.get("args")
  109. kwargs = value.get("kwargs")
  110. for method in methods:
  111. try:
  112. if isclass(method) and issubclass(method, BaseException):
  113. return None
  114. if args and kwargs:
  115. return method(*args, **kwargs)
  116. elif args:
  117. return method(*args)
  118. elif kwargs:
  119. return method(**kwargs)
  120. else:
  121. return method()
  122. except Exception:
  123. continue
  124. except Exception:
  125. return None
  126. def _check_allowed_modules(self, value: dict[str, Any]) -> None:
  127. needed = tuple(value["id"])
  128. method = value.get("method")
  129. if isinstance(method, list):
  130. method_display = ",".join(m or "<init>" for m in method)
  131. elif isinstance(method, str):
  132. method_display = method
  133. else:
  134. method_display = "<init>"
  135. dotted = ".".join(needed)
  136. if not self._allowed_modules:
  137. raise InvalidModuleError(
  138. f"Refused to deserialize JSON constructor: {dotted} (method: {method_display}). "
  139. "No allowed_json_modules configured.\n\n"
  140. "Unblock with ONE of:\n"
  141. f" • JsonPlusSerializer(allowed_json_modules=[{needed!r}, ...])\n"
  142. " • (DANGEROUS) JsonPlusSerializer(allowed_json_modules=True)\n\n"
  143. "Note: Prefix allowlists are intentionally unsupported; prefer exact symbols "
  144. "or plain-JSON representations revived without import-time side effects."
  145. )
  146. if self._allowed_modules is True:
  147. return
  148. if needed in self._allowed_modules:
  149. return
  150. raise InvalidModuleError(
  151. f"Refused to deserialize JSON constructor: {dotted} (method: {method_display}). "
  152. "Symbol is not in the deserialization allowlist.\n\n"
  153. "Add exactly this symbol to unblock:\n"
  154. f" JsonPlusSerializer(allowed_json_modules=[{needed!r}, ...])\n"
  155. "Or, as a last resort (DANGEROUS):\n"
  156. " JsonPlusSerializer(allowed_json_modules=True)"
  157. )
  158. def dumps_typed(self, obj: Any) -> tuple[str, bytes]:
  159. if obj is None:
  160. return "null", EMPTY_BYTES
  161. elif isinstance(obj, bytes):
  162. return "bytes", obj
  163. elif isinstance(obj, bytearray):
  164. return "bytearray", obj
  165. else:
  166. try:
  167. return "msgpack", _msgpack_enc(obj)
  168. except ormsgpack.MsgpackEncodeError as exc:
  169. if self.pickle_fallback:
  170. return "pickle", pickle.dumps(obj)
  171. raise exc
  172. def loads_typed(self, data: tuple[str, bytes]) -> Any:
  173. type_, data_ = data
  174. if type_ == "null":
  175. return None
  176. elif type_ == "bytes":
  177. return data_
  178. elif type_ == "bytearray":
  179. return bytearray(data_)
  180. elif type_ == "json":
  181. return json.loads(data_, object_hook=self._reviver)
  182. elif type_ == "msgpack":
  183. return ormsgpack.unpackb(
  184. data_, ext_hook=self._unpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  185. )
  186. elif self.pickle_fallback and type_ == "pickle":
  187. return pickle.loads(data_)
  188. else:
  189. raise NotImplementedError(f"Unknown serialization type: {type_}")
  190. # --- msgpack ---
  191. EXT_CONSTRUCTOR_SINGLE_ARG = 0
  192. EXT_CONSTRUCTOR_POS_ARGS = 1
  193. EXT_CONSTRUCTOR_KW_ARGS = 2
  194. EXT_METHOD_SINGLE_ARG = 3
  195. EXT_PYDANTIC_V1 = 4
  196. EXT_PYDANTIC_V2 = 5
  197. EXT_NUMPY_ARRAY = 6
  198. def _msgpack_default(obj: Any) -> str | ormsgpack.Ext:
  199. if hasattr(obj, "model_dump") and callable(obj.model_dump): # pydantic v2
  200. return ormsgpack.Ext(
  201. EXT_PYDANTIC_V2,
  202. _msgpack_enc(
  203. (
  204. obj.__class__.__module__,
  205. obj.__class__.__name__,
  206. obj.model_dump(),
  207. "model_validate_json",
  208. ),
  209. ),
  210. )
  211. elif hasattr(obj, "get_secret_value") and callable(obj.get_secret_value):
  212. return ormsgpack.Ext(
  213. EXT_CONSTRUCTOR_SINGLE_ARG,
  214. _msgpack_enc(
  215. (
  216. obj.__class__.__module__,
  217. obj.__class__.__name__,
  218. obj.get_secret_value(),
  219. ),
  220. ),
  221. )
  222. elif hasattr(obj, "dict") and callable(obj.dict): # pydantic v1
  223. return ormsgpack.Ext(
  224. EXT_PYDANTIC_V1,
  225. _msgpack_enc(
  226. (
  227. obj.__class__.__module__,
  228. obj.__class__.__name__,
  229. obj.dict(),
  230. ),
  231. ),
  232. )
  233. elif hasattr(obj, "_asdict") and callable(obj._asdict): # namedtuple
  234. return ormsgpack.Ext(
  235. EXT_CONSTRUCTOR_KW_ARGS,
  236. _msgpack_enc(
  237. (
  238. obj.__class__.__module__,
  239. obj.__class__.__name__,
  240. obj._asdict(),
  241. ),
  242. ),
  243. )
  244. elif isinstance(obj, pathlib.Path):
  245. return ormsgpack.Ext(
  246. EXT_CONSTRUCTOR_POS_ARGS,
  247. _msgpack_enc(
  248. (obj.__class__.__module__, obj.__class__.__name__, obj.parts),
  249. ),
  250. )
  251. elif isinstance(obj, re.Pattern):
  252. return ormsgpack.Ext(
  253. EXT_CONSTRUCTOR_POS_ARGS,
  254. _msgpack_enc(
  255. ("re", "compile", (obj.pattern, obj.flags)),
  256. ),
  257. )
  258. elif isinstance(obj, UUID):
  259. return ormsgpack.Ext(
  260. EXT_CONSTRUCTOR_SINGLE_ARG,
  261. _msgpack_enc(
  262. (obj.__class__.__module__, obj.__class__.__name__, obj.hex),
  263. ),
  264. )
  265. elif isinstance(obj, decimal.Decimal):
  266. return ormsgpack.Ext(
  267. EXT_CONSTRUCTOR_SINGLE_ARG,
  268. _msgpack_enc(
  269. (obj.__class__.__module__, obj.__class__.__name__, str(obj)),
  270. ),
  271. )
  272. elif isinstance(obj, (set, frozenset, deque)):
  273. return ormsgpack.Ext(
  274. EXT_CONSTRUCTOR_SINGLE_ARG,
  275. _msgpack_enc(
  276. (obj.__class__.__module__, obj.__class__.__name__, tuple(obj)),
  277. ),
  278. )
  279. elif isinstance(obj, (IPv4Address, IPv4Interface, IPv4Network)):
  280. return ormsgpack.Ext(
  281. EXT_CONSTRUCTOR_SINGLE_ARG,
  282. _msgpack_enc(
  283. (obj.__class__.__module__, obj.__class__.__name__, str(obj)),
  284. ),
  285. )
  286. elif isinstance(obj, (IPv6Address, IPv6Interface, IPv6Network)):
  287. return ormsgpack.Ext(
  288. EXT_CONSTRUCTOR_SINGLE_ARG,
  289. _msgpack_enc(
  290. (obj.__class__.__module__, obj.__class__.__name__, str(obj)),
  291. ),
  292. )
  293. elif isinstance(obj, datetime):
  294. return ormsgpack.Ext(
  295. EXT_METHOD_SINGLE_ARG,
  296. _msgpack_enc(
  297. (
  298. obj.__class__.__module__,
  299. obj.__class__.__name__,
  300. obj.isoformat(),
  301. "fromisoformat",
  302. ),
  303. ),
  304. )
  305. elif isinstance(obj, timedelta):
  306. return ormsgpack.Ext(
  307. EXT_CONSTRUCTOR_POS_ARGS,
  308. _msgpack_enc(
  309. (
  310. obj.__class__.__module__,
  311. obj.__class__.__name__,
  312. (obj.days, obj.seconds, obj.microseconds),
  313. ),
  314. ),
  315. )
  316. elif isinstance(obj, date):
  317. return ormsgpack.Ext(
  318. EXT_CONSTRUCTOR_POS_ARGS,
  319. _msgpack_enc(
  320. (
  321. obj.__class__.__module__,
  322. obj.__class__.__name__,
  323. (obj.year, obj.month, obj.day),
  324. ),
  325. ),
  326. )
  327. elif isinstance(obj, time):
  328. return ormsgpack.Ext(
  329. EXT_CONSTRUCTOR_KW_ARGS,
  330. _msgpack_enc(
  331. (
  332. obj.__class__.__module__,
  333. obj.__class__.__name__,
  334. {
  335. "hour": obj.hour,
  336. "minute": obj.minute,
  337. "second": obj.second,
  338. "microsecond": obj.microsecond,
  339. "tzinfo": obj.tzinfo,
  340. "fold": obj.fold,
  341. },
  342. ),
  343. ),
  344. )
  345. elif isinstance(obj, timezone):
  346. return ormsgpack.Ext(
  347. EXT_CONSTRUCTOR_POS_ARGS,
  348. _msgpack_enc(
  349. (
  350. obj.__class__.__module__,
  351. obj.__class__.__name__,
  352. obj.__getinitargs__(), # type: ignore[attr-defined]
  353. ),
  354. ),
  355. )
  356. elif isinstance(obj, ZoneInfo):
  357. return ormsgpack.Ext(
  358. EXT_CONSTRUCTOR_SINGLE_ARG,
  359. _msgpack_enc(
  360. (obj.__class__.__module__, obj.__class__.__name__, obj.key),
  361. ),
  362. )
  363. elif isinstance(obj, Enum):
  364. return ormsgpack.Ext(
  365. EXT_CONSTRUCTOR_SINGLE_ARG,
  366. _msgpack_enc(
  367. (obj.__class__.__module__, obj.__class__.__name__, obj.value),
  368. ),
  369. )
  370. elif isinstance(obj, SendProtocol):
  371. return ormsgpack.Ext(
  372. EXT_CONSTRUCTOR_POS_ARGS,
  373. _msgpack_enc(
  374. (obj.__class__.__module__, obj.__class__.__name__, (obj.node, obj.arg)),
  375. ),
  376. )
  377. elif dataclasses.is_dataclass(obj):
  378. # doesn't use dataclasses.asdict to avoid deepcopy and recursion
  379. return ormsgpack.Ext(
  380. EXT_CONSTRUCTOR_KW_ARGS,
  381. _msgpack_enc(
  382. (
  383. obj.__class__.__module__,
  384. obj.__class__.__name__,
  385. {
  386. field.name: getattr(obj, field.name)
  387. for field in dataclasses.fields(obj)
  388. },
  389. ),
  390. ),
  391. )
  392. elif isinstance(obj, Item):
  393. return ormsgpack.Ext(
  394. EXT_CONSTRUCTOR_KW_ARGS,
  395. _msgpack_enc(
  396. (
  397. obj.__class__.__module__,
  398. obj.__class__.__name__,
  399. {k: getattr(obj, k) for k in obj.__slots__},
  400. ),
  401. ),
  402. )
  403. elif (np_mod := sys.modules.get("numpy")) is not None and isinstance(
  404. obj, np_mod.ndarray
  405. ):
  406. order = "F" if obj.flags.f_contiguous and not obj.flags.c_contiguous else "C"
  407. if obj.flags.c_contiguous:
  408. mv = memoryview(obj)
  409. try:
  410. meta = (obj.dtype.str, obj.shape, order, mv)
  411. return ormsgpack.Ext(EXT_NUMPY_ARRAY, _msgpack_enc(meta))
  412. finally:
  413. mv.release()
  414. else:
  415. buf = obj.tobytes(order="A")
  416. meta = (obj.dtype.str, obj.shape, order, buf)
  417. return ormsgpack.Ext(EXT_NUMPY_ARRAY, _msgpack_enc(meta))
  418. elif isinstance(obj, BaseException):
  419. return repr(obj)
  420. else:
  421. raise TypeError(f"Object of type {obj.__class__.__name__} is not serializable")
  422. def _msgpack_ext_hook(code: int, data: bytes) -> Any:
  423. if code == EXT_CONSTRUCTOR_SINGLE_ARG:
  424. try:
  425. tup = ormsgpack.unpackb(
  426. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  427. )
  428. # module, name, arg
  429. return getattr(importlib.import_module(tup[0]), tup[1])(tup[2])
  430. except Exception:
  431. return
  432. elif code == EXT_CONSTRUCTOR_POS_ARGS:
  433. try:
  434. tup = ormsgpack.unpackb(
  435. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  436. )
  437. # module, name, args
  438. return getattr(importlib.import_module(tup[0]), tup[1])(*tup[2])
  439. except Exception:
  440. return
  441. elif code == EXT_CONSTRUCTOR_KW_ARGS:
  442. try:
  443. tup = ormsgpack.unpackb(
  444. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  445. )
  446. # module, name, args
  447. return getattr(importlib.import_module(tup[0]), tup[1])(**tup[2])
  448. except Exception:
  449. return
  450. elif code == EXT_METHOD_SINGLE_ARG:
  451. try:
  452. tup = ormsgpack.unpackb(
  453. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  454. )
  455. # module, name, arg, method
  456. return getattr(getattr(importlib.import_module(tup[0]), tup[1]), tup[3])(
  457. tup[2]
  458. )
  459. except Exception:
  460. return
  461. elif code == EXT_PYDANTIC_V1:
  462. try:
  463. tup = ormsgpack.unpackb(
  464. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  465. )
  466. # module, name, kwargs
  467. cls = getattr(importlib.import_module(tup[0]), tup[1])
  468. try:
  469. return cls(**tup[2])
  470. except Exception:
  471. return cls.construct(**tup[2])
  472. except Exception:
  473. # for pydantic objects we can't find/reconstruct
  474. # let's return the kwargs dict instead
  475. try:
  476. return tup[2]
  477. except NameError:
  478. return
  479. elif code == EXT_PYDANTIC_V2:
  480. try:
  481. tup = ormsgpack.unpackb(
  482. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  483. )
  484. # module, name, kwargs, method
  485. cls = getattr(importlib.import_module(tup[0]), tup[1])
  486. try:
  487. return cls(**tup[2])
  488. except Exception:
  489. return cls.model_construct(**tup[2])
  490. except Exception:
  491. # for pydantic objects we can't find/reconstruct
  492. # let's return the kwargs dict instead
  493. try:
  494. return tup[2]
  495. except NameError:
  496. return
  497. elif code == EXT_NUMPY_ARRAY:
  498. try:
  499. import numpy as _np
  500. dtype_str, shape, order, buf = ormsgpack.unpackb(
  501. data, ext_hook=_msgpack_ext_hook, option=ormsgpack.OPT_NON_STR_KEYS
  502. )
  503. arr = _np.frombuffer(buf, dtype=_np.dtype(dtype_str))
  504. return arr.reshape(shape, order=order)
  505. except Exception:
  506. return
  507. def _msgpack_ext_hook_to_json(code: int, data: bytes) -> Any:
  508. if code == EXT_CONSTRUCTOR_SINGLE_ARG:
  509. try:
  510. tup = ormsgpack.unpackb(
  511. data,
  512. ext_hook=_msgpack_ext_hook_to_json,
  513. option=ormsgpack.OPT_NON_STR_KEYS,
  514. )
  515. if tup[0] == "uuid" and tup[1] == "UUID":
  516. hex_ = tup[2]
  517. return (
  518. f"{hex_[:8]}-{hex_[8:12]}-{hex_[12:16]}-{hex_[16:20]}-{hex_[20:]}"
  519. )
  520. # module, name, arg
  521. return tup[2]
  522. except Exception:
  523. return
  524. elif code == EXT_CONSTRUCTOR_POS_ARGS:
  525. try:
  526. tup = ormsgpack.unpackb(
  527. data,
  528. ext_hook=_msgpack_ext_hook_to_json,
  529. option=ormsgpack.OPT_NON_STR_KEYS,
  530. )
  531. if tup[0] == "langgraph.types" and tup[1] == "Send":
  532. from langgraph.types import Send # type: ignore
  533. return Send(*tup[2])
  534. # module, name, args
  535. return tup[2]
  536. except Exception:
  537. return
  538. elif code == EXT_CONSTRUCTOR_KW_ARGS:
  539. try:
  540. tup = ormsgpack.unpackb(
  541. data,
  542. ext_hook=_msgpack_ext_hook_to_json,
  543. option=ormsgpack.OPT_NON_STR_KEYS,
  544. )
  545. # module, name, args
  546. return tup[2]
  547. except Exception:
  548. return
  549. elif code == EXT_METHOD_SINGLE_ARG:
  550. try:
  551. tup = ormsgpack.unpackb(
  552. data,
  553. ext_hook=_msgpack_ext_hook_to_json,
  554. option=ormsgpack.OPT_NON_STR_KEYS,
  555. )
  556. # module, name, arg, method
  557. return tup[2]
  558. except Exception:
  559. return
  560. elif code == EXT_PYDANTIC_V1:
  561. try:
  562. tup = ormsgpack.unpackb(
  563. data,
  564. ext_hook=_msgpack_ext_hook_to_json,
  565. option=ormsgpack.OPT_NON_STR_KEYS,
  566. )
  567. # module, name, kwargs
  568. return tup[2]
  569. except Exception:
  570. # for pydantic objects we can't find/reconstruct
  571. # let's return the kwargs dict instead
  572. return
  573. elif code == EXT_PYDANTIC_V2:
  574. try:
  575. tup = ormsgpack.unpackb(
  576. data,
  577. ext_hook=_msgpack_ext_hook_to_json,
  578. option=ormsgpack.OPT_NON_STR_KEYS,
  579. )
  580. # module, name, kwargs, method
  581. return tup[2]
  582. except Exception:
  583. return
  584. elif code == EXT_NUMPY_ARRAY:
  585. try:
  586. import numpy as _np
  587. dtype_str, shape, order, buf = ormsgpack.unpackb(
  588. data,
  589. ext_hook=_msgpack_ext_hook_to_json,
  590. option=ormsgpack.OPT_NON_STR_KEYS,
  591. )
  592. arr = _np.frombuffer(buf, dtype=_np.dtype(dtype_str))
  593. return arr.reshape(shape, order=order).tolist()
  594. except Exception:
  595. return
  596. class InvalidModuleError(Exception):
  597. """Exception raised when a module is not in the allowlist."""
  598. def __init__(self, message: str):
  599. self.message = message
  600. _option = (
  601. ormsgpack.OPT_NON_STR_KEYS
  602. | ormsgpack.OPT_PASSTHROUGH_DATACLASS
  603. | ormsgpack.OPT_PASSTHROUGH_DATETIME
  604. | ormsgpack.OPT_PASSTHROUGH_ENUM
  605. | ormsgpack.OPT_PASSTHROUGH_UUID
  606. | ormsgpack.OPT_REPLACE_SURROGATES
  607. )
  608. def _msgpack_enc(data: Any) -> bytes:
  609. return ormsgpack.packb(data, default=_msgpack_default, option=_option)