Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions marimo/_save/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from marimo._runtime.state import State
from marimo._save.hash import HashKey
from marimo._save.loaders import Loader
from marimo._save.loaders.lazy import LazyLoader
from marimo._save.stores import Store

# NB. Increment on cache breaking changes.
Expand Down Expand Up @@ -76,6 +77,10 @@ class CacheState:

store: Store
hash_memo: dict[str, bytes] = field(default_factory=dict)
# Lazy-store session state; see `loaders/lazy.py:_cache_state`.
active_lazy_loaders: dict[str, LazyLoader] = field(default_factory=dict)
poisoned_keys: set[str] = field(default_factory=set)
wasm_dict_store: Store | None = None

def is_memoizable(self, value: Any) -> bool:
"""Whether *value* is eligible for content-hash memoization.
Expand Down
33 changes: 30 additions & 3 deletions marimo/_save/loaders/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Copyright 2026 Marimo. All rights reserved.
from __future__ import annotations

from dataclasses import dataclass
from typing import Literal

from marimo._save.loaders.json import JsonLoader
from marimo._save.loaders.lazy import LazyLoader
from marimo._save.loaders.lazy import LazyLoader, WasmLazyLoader
from marimo._save.loaders.loader import (
BasePersistenceLoader,
Loader,
Expand All @@ -13,18 +14,42 @@
)
from marimo._save.loaders.memory import MemoryLoader
from marimo._save.loaders.pickle import PickleLoader
from marimo._utils.platform import is_pyodide

LoaderKey = Literal["memory", "pickle", "json", "lazy"]

PERSISTENT_LOADERS: dict[LoaderKey, LoaderType] = {

@dataclass(frozen=True)
class DualLoader:
"""A loader registered as a native/WASM pair under one name.

`resolve()` performs the *single* environment check and returns the
concrete loader class, so nothing downstream re-checks the platform.
Any loader can opt into dual behavior by registering one of these.
"""

native: LoaderType
wasm: LoaderType

def resolve(self) -> LoaderType:
return self.wasm if is_pyodide() else self.native


def resolve_loader(entry: LoaderType | DualLoader) -> LoaderType:
"""Resolve a registry entry to a concrete loader class."""
return entry.resolve() if isinstance(entry, DualLoader) else entry


PERSISTENT_LOADERS: dict[LoaderKey, LoaderType | DualLoader] = {
"pickle": PickleLoader,
"json": JsonLoader,
"lazy": LazyLoader,
"lazy": DualLoader(native=LazyLoader, wasm=WasmLazyLoader),
}

__all__ = [
"PERSISTENT_LOADERS",
"BasePersistenceLoader",
"DualLoader",
"JsonLoader",
"LazyLoader",
"Loader",
Expand All @@ -33,4 +58,6 @@
"LoaderType",
"MemoryLoader",
"PickleLoader",
"WasmLazyLoader",
"resolve_loader",
]
Loading
Loading