Coverage for open_webui/utils/json_codec.py: 19%
39 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 05:07 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 05:07 +0000
1"""The app-wide JSON codec, selected by the ``ENABLE_ORJSON`` env var.
3Every module that would otherwise reach for stdlib ``json`` imports ``JSONCodec``
4from here, so the whole app switches implementation from a single flag. With the
5flag off these are stdlib ``json`` and engineio's codec verbatim, so the default
6behaviour is exactly what it was before orjson entered the picture. ``dumps_bytes``
7returns UTF-8 bytes for sinks that re-parse the payload; under orjson it skips
8both the str round trip and the line-separator escaping ``dumps`` applies, so
9never feed it to line-framed output such as SSE.
10"""
12from __future__ import annotations
14import json as stdlib_json
16from engineio import json as engineio_json
17from open_webui.env import ENABLE_ORJSON
19if ENABLE_ORJSON: 19 ↛ 20line 19 didn't jump to line 20 because the condition on line 19 was never true
20 import orjson
22 # Module-level because CPython rebuilds these dicts on every call.
23 FAST_PATH_KWARGS = ({'separators': (',', ':')}, {'ensure_ascii': False})
25 class ORJSONCodec:
26 """stdlib-``json``-compatible codec backed by orjson.
28 The fast path is not byte-for-byte stdlib: it is always compact, formats
29 floats orjson's way (``1e16``, not ``1e+16``), and is raw UTF-8 apart from
30 the three line separators ``dumps`` escapes, so a ``separators`` caller loses
31 stdlib's ASCII escaping and an ``ensure_ascii=False`` caller loses its
32 spacing. ``dumps`` also serializes ``datetime``/``UUID``/dataclasses that
33 stdlib refuses, and encodes ``NaN``/``Infinity`` as ``null``. ``loads``
34 decodes integers above ``2**64-1`` or below ``-2**63`` as ``float`` and does
35 not enforce engineio's 100-digit integer-literal limit.
37 What orjson does reject (non-str dict keys and oversized ints on ``dumps``,
38 the ``NaN``/``Infinity`` literals on ``loads``) falls back to engineio's
39 stdlib-based codec, and with it stdlib's formatting.
40 """
42 JSONDecodeError = engineio_json.JSONDecodeError
44 @staticmethod
45 def dumps(obj, *args, **kwargs):
46 if args or (kwargs and kwargs not in FAST_PATH_KWARGS):
47 return engineio_json.dumps(obj, *args, **kwargs)
48 try:
49 serialized = orjson.dumps(obj).decode('utf-8')
50 except (TypeError, ValueError):
51 return engineio_json.dumps(obj, *args, **kwargs)
52 # Raw, these three split an SSE frame reassembled with ``splitlines()``.
53 # A dict-table translate walks char by char; chained replace runs on C fast paths.
54 if '\u2028' in serialized or '\u2029' in serialized or '\x85' in serialized:
55 return serialized.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029').replace('\x85', '\\u0085')
56 return serialized
58 @staticmethod
59 def loads(s, *args, **kwargs):
60 if args or kwargs:
61 return engineio_json.loads(s, *args, **kwargs)
62 try:
63 return orjson.loads(s)
64 except (TypeError, ValueError):
65 return engineio_json.loads(s, *args, **kwargs)
67 # Drop-in for stdlib ``json``: ``JSONCodec.dumps`` / ``JSONCodec.loads``.
68 JSONCodec = ORJSONCodec
69 # Codec handed to the socket.io/engineio managers, which default to their own.
70 SOCKETIO_JSON = ORJSONCodec
72 def dumps_bytes(obj) -> bytes:
73 """JSON as UTF-8 bytes, skipping the str round trip and the escaping ``dumps`` does."""
74 try:
75 return orjson.dumps(obj)
76 except (TypeError, ValueError):
77 return engineio_json.dumps(obj).encode('utf-8')
78else:
79 JSONCodec = stdlib_json
80 SOCKETIO_JSON = engineio_json
82 def dumps_bytes(obj) -> bytes:
83 """JSON as UTF-8 bytes; here simply ``dumps`` encoded."""
84 return stdlib_json.dumps(obj).encode('utf-8')