Skip to content

Commit 82bf19b

Browse files
authored
Remove dual JSON encoding in pyodide message callback (#6350)
The pyodide bootstrap was encoding kernel messages twice. First via `encode_json_bytes()` for the data payload, then again via `encode_json_str()` for the entire message. This resulted in the data field being base64-encoded instead of a proper JSON object. Fixed by manually constructing the JSON string to avoid the second encoding, and added a test to prevent regression.
1 parent 5056b2a commit 82bf19b

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

marimo/_pyodide/bootstrap.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
from typing import TYPE_CHECKING, Callable
66

77
from marimo._config.config import merge_config
8-
from marimo._messaging.msgspec_encoder import (
9-
encode_json_bytes,
10-
encode_json_str,
11-
)
8+
from marimo._messaging.msgspec_encoder import encode_json_bytes
129
from marimo._messaging.ops import KernelCapabilities, KernelReady
1310
from marimo._runtime.requests import (
1411
AppMetadata,
@@ -81,7 +78,9 @@ def create_session(
8178
"""
8279

8380
def write_kernel_message(op: KernelMessage) -> None:
84-
message_callback(encode_json_str({"op": op[0], "data": op[1]}))
81+
op_name, data_bytes = op
82+
text = f'{{"op": "{op_name}", "data": {data_bytes.decode("utf-8")}}}'
83+
message_callback(text)
8584

8685
# Lazy import to decrease startup time
8786
from marimo._config.config import merge_default_config

tests/_pyodide/test_bootstrap.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright 2024 Marimo. All rights reserved.
22
from __future__ import annotations
33

4+
import json
45
from typing import TYPE_CHECKING, Any, Callable
56

67
import pytest
@@ -176,3 +177,22 @@ def test_save_file(
176177
# Verify the file was saved correctly
177178
saved_content = mock_app_file.read_text()
178179
assert "print('hello')" in saved_content
180+
181+
182+
async def test_message_callback_format(
183+
mock_app_file: Path,
184+
) -> None:
185+
"""Test that message_callback receives properly formatted JSON."""
186+
received_messages: list[str] = []
187+
188+
session, _ = create_session(
189+
filename=str(mock_app_file),
190+
query_params={},
191+
message_callback=lambda text: received_messages.append(text),
192+
user_config=DEFAULT_CONFIG,
193+
)
194+
195+
assert len(received_messages) >= 1
196+
parsed = json.loads(received_messages[0])
197+
assert parsed["op"] == "kernel-ready"
198+
assert isinstance(parsed["data"], dict)

0 commit comments

Comments
 (0)