FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

"""Cross-platform key/value persistence (``AsyncStorage``). Mirrors React Native's ``AsyncStorage`` API but with native ``async`` coroutines and a JSON convenience layer. Values are persisted by the native ``Storage`` module: - **iOS**: ``NSUserDefaults`` (standard user defaults). - **Android**: ``SharedPreferences`` (file ``"pn_async_storage"``). - **Browser preview / tests**: an in-memory dict optionally backed by a JSON file under ``PN_STORAGE_DIR`` for inter-run persistence during local development (see [`FallbackStorage`][pythonnative.native_modules.fallback.FallbackStorage]). Every operation is a coroutine. The platform stores answer in microseconds, so calls complete inline on the framework loop without a thread hop. Example: ```python import pythonnative as pn async def remember_user(user): await pn.AsyncStorage.set_json("current_user", user.to_dict()) async def restore_session(): user_dict = await pn.AsyncStorage.get_json("current_user") if user_dict is None: return None return User.from_dict(user_dict) ``` """ from __future__ import annotations import json from typing import Any, Callable, List, Optional, Tuple, TypeVar from .native_modules.registry import NativeModule, native_module T = TypeVar("T") def _storage() -> NativeModule: return native_module("Storage") class AsyncStorage: """Async key/value persistence layered on platform-native stores. Every method is a coroutine that returns when the underlying native operation completes. Strings round-trip directly via [`get`][pythonnative.storage.AsyncStorage.get] / [`set`][pythonnative.storage.AsyncStorage.set]; richer values can use [`get_json`][pythonnative.storage.AsyncStorage.get_json] / [`set_json`][pythonnative.storage.AsyncStorage.set_json] which add a JSON encode/decode step. """ @staticmethod async def get(key: str) -> Optional[str]: """Return the string stored at ``key``, or ``None`` if missing.""" value = _storage().call("get", key=key) return None if value is None else str(value) @staticmethod async def set(key: str, value: str) -> None: """Persist ``value`` under ``key``. ``value`` must be a ``str``. For non-string values, use [`set_json`][pythonnative.storage.AsyncStorage.set_json]. """ if not isinstance(value, str): raise TypeError("AsyncStorage.set requires a str value; use set_json for richer types") _storage().call("set", key=key, value=value) @staticmethod async def delete(key: str) -> None: """Remove the entry at ``key`` if present (no-op otherwise).""" _storage().call("delete", key=key) @staticmethod async def all_keys() -> List[str]: """Return every persisted key (order is platform-dependent).""" keys = _storage().call("all_keys") return [str(k) for k in keys] if isinstance(keys, list) else [] @staticmethod async def clear() -> None: """Remove every entry written through ``AsyncStorage``.""" _storage().call("clear") @staticmethod async def get_json(key: str) -> Any: """Return the JSON-decoded value stored at ``key``, or ``None``. If the stored value isn't valid JSON, returns ``None`` rather than raising; assume the entry was written by another process or an older version of the app. """ raw = await AsyncStorage.get(key) if raw is None: return None try: return json.loads(raw) except (json.JSONDecodeError, TypeError): return None @staticmethod async def set_json(key: str, value: Any) -> None: """JSON-encode ``value`` and persist it under ``key``.""" await AsyncStorage.set(key, json.dumps(value, default=str)) def use_persisted_state( key: str, initial: T, ) -> Tuple[T, Callable[[Any], None]]: """Persisted [`use_state`][pythonnative.hooks.use_state] variant. Backed by [`AsyncStorage`][pythonnative.storage.AsyncStorage]: behaves like ``use_state`` but loads the prior value (if any) on mount and persists every subsequent update. Until the load completes the value is ``initial``, the same fallback React Native users get with ``AsyncStorage.getItem``. The setter accepts either a value or a ``current -> new`` callable, matching [`use_state`][pythonnative.use_state]. Writes are fire-and-forget; failures are silently absorbed (storage is best-effort by design). Args: key: Storage key. Pass a stable, namespaced string (e.g. ``"settings.theme"``). initial: Value used before the first load completes. Returns: ``(value, setter)``, same shape as [`use_state`][pythonnative.use_state]. Example: ```python import pythonnative as pn @pn.component def ThemeToggle(): theme, set_theme = pn.use_persisted_state("settings.theme", "light") return pn.Button( f"Theme: {theme}", on_press=lambda: set_theme("dark" if theme == "light" else "light"), ) ``` """ from .hooks import use_callback, use_effect, use_ref, use_state from .runtime import run_async state, set_state = use_state(initial) loaded = use_ref(False) async def _load() -> None: stored = await AsyncStorage.get_json(key) if stored is not None: set_state(stored) loaded.current = True use_effect(_load, [key]) def setter(value_or_updater: Any) -> None: def _reducer(current: Any) -> Any: new_value = value_or_updater(current) if callable(value_or_updater) else value_or_updater if loaded.current is True: run_async(AsyncStorage.set_json(key, new_value)) return new_value set_state(_reducer) stable_setter = use_callback(setter, [key]) return state, stable_setter __all__ = ["AsyncStorage", "use_persisted_state"]

Back | FazBrowse Home | New Git URL