add orjson - #6116
Conversation
Merging this PR will not alter performance
Performance Changes
Comparing Footnotes
|
Greptile SummaryThis PR adds optional orjson support for Reflex JSON handling. The main changes are:
Confidence Score: 5/5This looks safe to merge.
|
| Filename | Overview |
|---|---|
| reflex/app.py | Updates socket JSON hooks and event payload decoding to use the new orjson helper functions. |
| packages/reflex-base/src/reflex_base/utils/format.py | Adds optional orjson serialization helpers and socket-specific handling for non-finite float values. |
| packages/reflex-base/src/reflex_base/.templates/web/utils/state.js | Adds frontend parsing and reviving logic for Reflex sentinel values in JSON payloads. |
| packages/reflex-components-core/src/reflex_components_core/core/_upload.py | Switches upload event decoding and streamed state updates to the new JSON helper path. |
Reviews (13): Last reviewed commit: "refactor: single TypeError fallback in o..." | Re-trigger Greptile
|
the issue with orjson is that we lose NaN handling which is relevant for numpy, which is why we were using JSON5 as well |
|
Yeah, that's why i did not update it in all places |
…llback Happy path uses native JSON.parse; the catch rewrites Python"s bare Infinity/-Infinity/NaN tokens outside string literals before retrying, so only payloads that actually contain specials pay the extra cost. NaN has no JSON literal and becomes null (matches JSON.stringify).
…o null Swap bare NaN for a sentinel string before JSON.parse and revive it back to a real NaN so Python-side float(nan) round-trips to the frontend.
|
related #6339 |
Apply orjson_dumps/orjson_loads helpers to the new reflex_base package locations after upstream's restructuring.
bed6d7b to
7555e7b
Compare
|
now that we've eliminated JSON5 in the frontend code, I think this PR is the next step for optimizing the serialization path. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18ae582a7f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Serialization previously walked every payload with _replace_non_finite_floats before every socket emit to protect NaN/Infinity floats (orjson silently emits null for them) and to escape sentinel-colliding strings. The walk dominated serialization cost (~95% on a 290KB payload) and ran even in the stdlib fallback, making default installs slower than before reflex-dev#6116. Both backends now dump once and decide from the output bytes whether the walk is needed at all: - orjson: None, NaN and Infinity all serialize to exactly the token null, so output without null provably lost no non-finite floats; output without the sentinel prefix has no colliding strings. Only when either substring appears is the payload walked and re-dumped. The default= callback is split into a non-walking pass-1 variant and a walking pass-2 variant so StateUpdate-wrapped payloads keep the fast path. - stdlib (no orjson): bare NaN/Infinity tokens are restored by the frontend's bare-token rewriter, so only a sentinel-prefix hit forces the walk. Restores default-install performance to parity with pre-reflex-dev#6116. Frontend mirror of the same idea: passing a reviver to JSON.parse disables the engine's native fast parser (measured 4-12x slower in Chromium), so parseNonFiniteAwareJSON only applies the reviver when the payload actually contains the sentinel prefix. The upload NDJSON parser gains the bare-token rewriter retry it was missing. Also: hoist the orjson import to module level (was per-call on the compile hot path), make orjson_dumps fall back to stdlib for indent widths other than 2 instead of silently coercing, and preserve kwargs in its TypeError fallback. Serialize-only medians vs stdlib baseline (1000x10 table delta): 1.91ms -> 431us with orjson, parity without. Browser decode of the same delta: 2.05ms -> 506us.
test_process_event_wire measures the full receive -> process -> serialize round trip: raw event JSON decoded via orjson_loads (as socket.io's decoder does), events processed through BaseStateEventProcessor with a real StateManagerMemory, and each delta serialized through orjson_dumps_socket the way App._setup_state wires socket.io's encoder. Parametrized with a small (5-row) and large (500-row) table delta so JSON parse/dump cost is part of the measured path, which no existing benchmark covered.
Serialization previously walked every payload with _replace_non_finite_floats before every socket emit to protect NaN/Infinity floats (orjson silently emits null for them) and to escape sentinel-colliding strings. The walk dominated serialization cost (~95% on a 290KB payload) and ran even in the stdlib fallback, making default installs slower than before reflex-dev#6116. Both backends now dump once and decide from the output bytes whether the walk is needed at all: - orjson: None, NaN and Infinity all serialize to exactly the token null, so output without null provably lost no non-finite floats; output without the sentinel prefix has no colliding strings. Only when either substring appears is the payload walked and re-dumped. The default= callback is split into a non-walking pass-1 variant and a walking pass-2 variant so StateUpdate-wrapped payloads keep the fast path. - stdlib (no orjson): bare NaN/Infinity tokens are restored by the frontend's bare-token rewriter, so only a sentinel-prefix hit forces the walk. Restores default-install performance to parity with pre-reflex-dev#6116. Frontend mirror of the same idea: passing a reviver to JSON.parse disables the engine's native fast parser (measured 4-12x slower in Chromium), so parseNonFiniteAwareJSON only applies the reviver when the payload actually contains the sentinel prefix. The upload NDJSON parser gains the bare-token rewriter retry it was missing. Also: hoist the orjson import to module level (was per-call on the compile hot path), make orjson_dumps fall back to stdlib for indent widths other than 2 instead of silently coercing, and preserve kwargs in its TypeError fallback. Serialize-only medians vs stdlib baseline (1000x10 table delta): 1.91ms -> 431us with orjson, parity without. Browser decode of the same delta: 2.05ms -> 506us.
The stdlib fallbacks (no orjson, or orjson TypeError e.g. >64-bit ints) walked the original packet, but _replace_non_finite_floats cannot traverse custom objects like the StateUpdate every real socket payload wraps. Strings produced by serializers.serialize therefore shipped unescaped, and the frontend reviver silently corrupted user data that collided with a sentinel: "__reflex_nan__" became NaN, "__reflex_esc__x" became "x". Route both fallbacks through _json_dumps_socket_fallback, whose re-dump passes a walking default= callback so serializer output is escaped the same way the orjson path's _default_walked already does. The fast path (single dump + prefix scan) is unchanged. Regression tests use the real wire shape ["event", StateUpdate(delta)] and fail against the previous implementation on all three paths.
Both orjson.dumps passes fell back to the same stdlib path on TypeError; one try block covering the whole orjson section says that once. Behavior unchanged: any payload orjson cannot represent (e.g. int > 64-bit) takes _json_dumps_socket_fallback regardless of which pass it surfaced in.
There was a problem hiding this comment.
16 issues found across 35 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/reflex-base/news/6116.performance.md">
<violation number="1" location="packages/reflex-base/news/6116.performance.md:1">
P3: This overstates behavior of the general JSON helpers: `json_dumps` always uses stdlib, and `orjson_dumps` converts `NaN`/`Infinity` to `null` rather than round-tripping them. Scoping the sentinel claim to `orjson_dumps_socket` avoids promising data preservation to callers of `orjson_dumps`.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/upload.js">
<violation number="1" location="packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/upload.js:3">
P3: Frontend module loading now has a direct circular dependency between `state.js` and `helpers/upload.js`, making initialization order fragile. Moving the non-finite JSON codec into a dependency-free utility (ideally exposing the complete parse-with-fallback operation) would keep both consumers acyclic.</violation>
</file>
<file name="reflex/utils/frontend_skeleton.py">
<violation number="1" location="reflex/utils/frontend_skeleton.py:421">
P2: Persisted `package.json` numeric fields above orjson's 64-bit range are silently rounded and rewritten, violating this flow's pass-through behavior and making output depend on whether optional `orjson` is installed. Retaining stdlib parsing here, or otherwise preserving out-of-range numbers exactly, would avoid corrupting user-owned fields.</violation>
</file>
<file name="packages/reflex-site-shared/src/reflex_site_shared/lib/meta/meta.py">
<violation number="1" location="packages/reflex-site-shared/src/reflex_site_shared/lib/meta/meta.py:3">
P1: Importing this meta module can now fail with `ImportError` for supported `reflex-site-shared` installations because `orjson_dumps` is new but the package does not require a `reflex-base`/`reflex` release that exports it. Please add an appropriate runtime minimum dependency (or retain a compatibility fallback) before using the symbol.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/plugins/shared_tailwind.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/plugins/shared_tailwind.py:94">
P2: Tailwind theme values or plugin arguments containing `NaN`/`Infinity` now become `null`, silently changing the generated JavaScript configuration. Since this output is JavaScript rather than strict JSON, consider retaining stdlib `json.dumps` here or using a serializer that preserves these valid JS numeric tokens.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/vars/number.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/vars/number.py:962">
P2: With orjson installed, non-finite or float-overflowing `Decimal` literals now serialize as `null`, silently changing a number into a null value instead of rejecting its invalid numeric representation. Checking the converted float for finiteness before `orjson_dumps` would keep `LiteralNumberVar.json()` consistent with the float path.</violation>
</file>
<file name="reflex/utils/path_ops.py">
<violation number="1" location="reflex/utils/path_ops.py:248">
P2: Existing files containing `NaN` or `Infinity` can no longer be updated because the previous `json.dump` path emitted those tokens while `orjson_loads` rejects them. A stdlib-compatible fallback for these legacy files would preserve `update_json_file` behavior.</violation>
<violation number="2" location="reflex/utils/path_ops.py:254">
P2: Updates containing non-finite floats now silently persist them as `null`, losing the distinction from a real `None`; the previous serializer preserved them as `NaN`/`Infinity`. This path should retain the old semantics or explicitly reject such values instead of silently corrupting them.</violation>
</file>
<file name="reflex/utils/exec.py">
<violation number="1" location="reflex/utils/exec.py:84">
P2: Valid package.json files containing an integer outside orjson's 64-bit range now crash frontend startup/change monitoring, although the previous `json.load` accepted them. Consider retaining stdlib parsing here or adding a stdlib fallback when `orjson_loads` rejects input that `json.loads` can parse.</violation>
</file>
<file name="reflex/utils/telemetry.py">
<violation number="1" location="reflex/utils/telemetry.py:135">
P2: CPU-info caching can now fail on systems whose locale encoding cannot represent a non-ASCII CPU manufacturer/model, because `orjson_dumps` emits those characters literally while `write_text` uses the locale encoding. Writing explicit UTF-8 bytes would preserve the new serializer’s output and match the byte-based read path.</violation>
</file>
<file name="reflex/utils/prerequisites.py">
<violation number="1" location="reflex/utils/prerequisites.py:117">
P1: With the `orjson` extra installed, reading reflex.json loses precision for its 128-bit UUID-derived `project_hash`; this path can then persist the rounded float, and telemetry cannot pass it to `uuid.UUID(int=...)`. These reflex.json reads should retain stdlib `json.loads` (or use a loader that preserves arbitrary-precision integers).</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/config.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/config.py:643">
P3: `Config.json()` never benefits from orjson because every `Config` contains the unsupported `_non_default_attributes` set, so `orjson_dumps` always retries the full serialization with `json_dumps`. Consider normalizing the config/custom fields for orjson or extending the helper with the registered serializer before switching this call site.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/.templates/web/utils/state.js">
<violation number="1" location="packages/reflex-base/src/reflex_base/.templates/web/utils/state.js:493">
P2: State values from a custom `App.sio` can be corrupted: legitimate `__reflex_nan__`, `__reflex_inf__`, or `__reflex_neg_inf__` strings are revived even though that server was never configured to escape collisions. Consider configuring provided `AsyncServer` instances with the sentinel-aware serializer, or marking sentinel-encoded packets before applying this reviver.</violation>
</file>
<file name="reflex/app.py">
<violation number="1" location="reflex/app.py:1669">
P2: Compiling a stateful route with a non-ASCII dynamic argument can now fail on systems whose default text encoding cannot represent it, because `orjson_dumps` emits Unicode directly into a locale-encoded file. Opening `stateful_pages_marker` with `encoding="utf-8"` would match the UTF-8 JSON output and its byte-based reader.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/utils/format.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/utils/format.py:713">
P3: `ensure_ascii=True` is silently ignored because this guard still selects orjson, which always emits UTF-8. Falling back to `json_dumps` when the option is truthy preserves the advertised keyword behavior.</violation>
<violation number="2" location="packages/reflex-base/src/reflex_base/utils/format.py:724">
P1: Reflex serializers are bypassed whenever orjson natively supports a value: `orjson_dumps` changes datetime formatting, and both fast paths skip custom Enum serializers. Route these native types through the serializer registry or retain `json_dumps` for them so generated values and socket deltas preserve existing semantics.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| """Meta module.""" | ||
|
|
||
| import json | ||
| from reflex_base.utils.format import orjson_dumps |
There was a problem hiding this comment.
P1: Importing this meta module can now fail with ImportError for supported reflex-site-shared installations because orjson_dumps is new but the package does not require a reflex-base/reflex release that exports it. Please add an appropriate runtime minimum dependency (or retain a compatibility fallback) before using the symbol.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-site-shared/src/reflex_site_shared/lib/meta/meta.py, line 3:
<comment>Importing this meta module can now fail with `ImportError` for supported `reflex-site-shared` installations because `orjson_dumps` is new but the package does not require a `reflex-base`/`reflex` release that exports it. Please add an appropriate runtime minimum dependency (or retain a compatibility fallback) before using the symbol.</comment>
<file context>
@@ -1,6 +1,6 @@
"""Meta module."""
-import json
+from reflex_base.utils.format import orjson_dumps
import reflex as rx
</file context>
| @@ -6,7 +6,6 @@ | |||
| import importlib | |||
There was a problem hiding this comment.
P1: With the orjson extra installed, reading reflex.json loses precision for its 128-bit UUID-derived project_hash; this path can then persist the rounded float, and telemetry cannot pass it to uuid.UUID(int=...). These reflex.json reads should retain stdlib json.loads (or use a loader that preserves arbitrary-precision integers).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At reflex/utils/prerequisites.py, line 117:
<comment>With the `orjson` extra installed, reading reflex.json loses precision for its 128-bit UUID-derived `project_hash`; this path can then persist the rounded float, and telemetry cannot pass it to `uuid.UUID(int=...)`. These reflex.json reads should retain stdlib `json.loads` (or use a loader that preserves arbitrary-precision integers).</comment>
<file context>
@@ -114,7 +114,7 @@ def get_or_set_last_reflex_version_check_datetime():
return None
# Open and read the file
- data = json.loads(reflex_json_file.read_text())
+ data = orjson_loads(reflex_json_file.read_bytes())
last_version_check_datetime = data.get("last_version_check_datetime")
if not last_version_check_datetime:
</file context>
| @@ -9,6 +9,11 @@ | |||
| from functools import lru_cache | |||
There was a problem hiding this comment.
P1: Reflex serializers are bypassed whenever orjson natively supports a value: orjson_dumps changes datetime formatting, and both fast paths skip custom Enum serializers. Route these native types through the serializer registry or retain json_dumps for them so generated values and socket deltas preserve existing semantics.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/src/reflex_base/utils/format.py, line 724:
<comment>Reflex serializers are bypassed whenever orjson natively supports a value: `orjson_dumps` changes datetime formatting, and both fast paths skip custom Enum serializers. Route these native types through the serializer registry or retain `json_dumps` for them so generated values and socket deltas preserve existing semantics.</comment>
<file context>
@@ -683,6 +688,210 @@ def json_dumps(obj: Any, **kwargs) -> str:
+ option |= orjson.OPT_SORT_KEYS
+
+ try:
+ return orjson.dumps(obj, option=option or None).decode()
+ except TypeError:
+ # json_dumps handles large integers and registered custom types.
</file context>
| try: | ||
| parsed = json.loads(root_package_json_path.read_text()) | ||
| except (json.JSONDecodeError, OSError) as e: | ||
| parsed = orjson_loads(root_package_json_path.read_bytes()) |
There was a problem hiding this comment.
P2: Persisted package.json numeric fields above orjson's 64-bit range are silently rounded and rewritten, violating this flow's pass-through behavior and making output depend on whether optional orjson is installed. Retaining stdlib parsing here, or otherwise preserving out-of-range numbers exactly, would avoid corrupting user-owned fields.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At reflex/utils/frontend_skeleton.py, line 421:
<comment>Persisted `package.json` numeric fields above orjson's 64-bit range are silently rounded and rewritten, violating this flow's pass-through behavior and making output depend on whether optional `orjson` is installed. Retaining stdlib parsing here, or otherwise preserving out-of-range numbers exactly, would avoid corrupting user-owned fields.</comment>
<file context>
@@ -418,8 +418,8 @@ def _read_persisted_package_json() -> dict:
try:
- parsed = json.loads(root_package_json_path.read_text())
- except (json.JSONDecodeError, OSError) as e:
+ parsed = orjson_loads(root_package_json_path.read_bytes())
+ except (ValueError, OSError) as e:
console.warn(
</file context>
| @@ -91,7 +91,7 @@ def tailwind_config_js_template( | |||
| Returns: | |||
There was a problem hiding this comment.
P2: Tailwind theme values or plugin arguments containing NaN/Infinity now become null, silently changing the generated JavaScript configuration. Since this output is JavaScript rather than strict JSON, consider retaining stdlib json.dumps here or using a serializer that preserves these valid JS numeric tokens.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/src/reflex_base/plugins/shared_tailwind.py, line 94:
<comment>Tailwind theme values or plugin arguments containing `NaN`/`Infinity` now become `null`, silently changing the generated JavaScript configuration. Since this output is JavaScript rather than strict JSON, consider retaining stdlib `json.dumps` here or using a serializer that preserves these valid JS numeric tokens.</comment>
<file context>
@@ -91,7 +91,7 @@ def tailwind_config_js_template(
The Tailwind config template.
"""
- import json
+ from reflex_base.utils.format import orjson_dumps
# Extract parameters
</file context>
| stateful_pages_marker.parent.mkdir(parents=True, exist_ok=True) | ||
| with stateful_pages_marker.open("w") as f: | ||
| json.dump(list(self._stateful_pages), f) | ||
| f.write(format.orjson_dumps(list(self._stateful_pages))) |
There was a problem hiding this comment.
P2: Compiling a stateful route with a non-ASCII dynamic argument can now fail on systems whose default text encoding cannot represent it, because orjson_dumps emits Unicode directly into a locale-encoded file. Opening stateful_pages_marker with encoding="utf-8" would match the UTF-8 JSON output and its byte-based reader.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At reflex/app.py, line 1669:
<comment>Compiling a stateful route with a non-ASCII dynamic argument can now fail on systems whose default text encoding cannot represent it, because `orjson_dumps` emits Unicode directly into a locale-encoded file. Opening `stateful_pages_marker` with `encoding="utf-8"` would match the UTF-8 JSON output and its byte-based reader.</comment>
<file context>
@@ -1667,7 +1666,7 @@ def _write_stateful_pages_marker(self):
stateful_pages_marker.parent.mkdir(parents=True, exist_ok=True)
with stateful_pages_marker.open("w") as f:
- json.dump(list(self._stateful_pages), f)
+ f.write(format.orjson_dumps(list(self._stateful_pages)))
def add_all_routes_endpoint(self):
</file context>
| @@ -0,0 +1 @@ | |||
| The JSON helpers in `reflex_base.utils.format` use orjson when installed, with sentinel-based round-tripping of `NaN`/`Infinity` floats to the frontend. Without orjson, socket serialization keeps the stdlib fast path: payloads are dumped once and only re-processed when a sentinel-colliding string is detected in the output. | |||
There was a problem hiding this comment.
P3: This overstates behavior of the general JSON helpers: json_dumps always uses stdlib, and orjson_dumps converts NaN/Infinity to null rather than round-tripping them. Scoping the sentinel claim to orjson_dumps_socket avoids promising data preservation to callers of orjson_dumps.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/news/6116.performance.md, line 1:
<comment>This overstates behavior of the general JSON helpers: `json_dumps` always uses stdlib, and `orjson_dumps` converts `NaN`/`Infinity` to `null` rather than round-tripping them. Scoping the sentinel claim to `orjson_dumps_socket` avoids promising data preservation to callers of `orjson_dumps`.</comment>
<file context>
@@ -0,0 +1 @@
+The JSON helpers in `reflex_base.utils.format` use orjson when installed, with sentinel-based round-tripping of `NaN`/`Infinity` floats to the frontend. Without orjson, socket serialization keeps the stdlib fast path: payloads are dumped once and only re-processed when a sentinel-colliding string is detected in the output.
</file context>
| import JSON5 from "json5"; | ||
| import env from "$/env.json"; | ||
| import { | ||
| parseNonFiniteAwareJSON, |
There was a problem hiding this comment.
P3: Frontend module loading now has a direct circular dependency between state.js and helpers/upload.js, making initialization order fragile. Moving the non-finite JSON codec into a dependency-free utility (ideally exposing the complete parse-with-fallback operation) would keep both consumers acyclic.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/upload.js, line 3:
<comment>Frontend module loading now has a direct circular dependency between `state.js` and `helpers/upload.js`, making initialization order fragile. Moving the non-finite JSON codec into a dependency-free utility (ideally exposing the complete parse-with-fallback operation) would keep both consumers acyclic.</comment>
<file context>
@@ -1,5 +1,9 @@
-import JSON5 from "json5";
import env from "$/env.json";
+import {
+ parseNonFiniteAwareJSON,
+ reviveNonFiniteFloats,
+ rewriteBareNonFiniteFloats,
</file context>
| from reflex_base.utils.serializers import serialize | ||
|
|
||
| return json.dumps(self, default=serialize) | ||
| return orjson_dumps(self) |
There was a problem hiding this comment.
P3: Config.json() never benefits from orjson because every Config contains the unsupported _non_default_attributes set, so orjson_dumps always retries the full serialization with json_dumps. Consider normalizing the config/custom fields for orjson or extending the helper with the registered serializer before switching this call site.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/src/reflex_base/config.py, line 643:
<comment>`Config.json()` never benefits from orjson because every `Config` contains the unsupported `_non_default_attributes` set, so `orjson_dumps` always retries the full serialization with `json_dumps`. Consider normalizing the config/custom fields for orjson or extending the helper with the registered serializer before switching this call site.</comment>
<file context>
@@ -638,11 +638,9 @@ def json(self) -> str:
- from reflex_base.utils.serializers import serialize
-
- return json.dumps(self, default=serialize)
+ return orjson_dumps(self)
@staticmethod
</file context>
| orjson is None | ||
| # orjson only supports two-space indentation and always emits UTF-8. | ||
| or indent not in (None, 2) | ||
| or kwargs.keys() - {"indent", "sort_keys", "ensure_ascii"} |
There was a problem hiding this comment.
P3: ensure_ascii=True is silently ignored because this guard still selects orjson, which always emits UTF-8. Falling back to json_dumps when the option is truthy preserves the advertised keyword behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-base/src/reflex_base/utils/format.py, line 713:
<comment>`ensure_ascii=True` is silently ignored because this guard still selects orjson, which always emits UTF-8. Falling back to `json_dumps` when the option is truthy preserves the advertised keyword behavior.</comment>
<file context>
@@ -683,6 +688,210 @@ def json_dumps(obj: Any, **kwargs) -> str:
+ orjson is None
+ # orjson only supports two-space indentation and always emits UTF-8.
+ or indent not in (None, 2)
+ or kwargs.keys() - {"indent", "sort_keys", "ensure_ascii"}
+ ):
+ return json_dumps(obj, **kwargs)
</file context>
| or kwargs.keys() - {"indent", "sort_keys", "ensure_ascii"} | |
| or kwargs.get("ensure_ascii", False) | |
| or kwargs.keys() - {"indent", "sort_keys", "ensure_ascii"} |
No description provided.