packages

Storage

A key-value store written in pure Python. Values live in a SQLite file in the app's private folder and in an in-memory copy, so reads are instant and writes are synchronous. It has the MMKV-style API (set, get_string, get_number) and the AsyncStorage-style API (get_item, set_item), plus hooks that keep a component in sync with a key.

  • pyxpo-storage
  • v0.1.0a1
  • Pure Python
  • Like react-native-mmkv and AsyncStorage

Install

Install the package into the same environment as pyxpo.

$pip install pyxpo-storage

Add storage to modules in your app config, then import it in your code.

pyproject.toml
[tool.pyxpo]
modules = ["storage"]
python
from pyxpo_storage import storage, use_storage_string

This module is pure Python. It still goes in modules so the build includes it in your app.

Platforms

iOSLibrary/Application Support/pyxpo-storage
Androidfiles/pyxpo-storage
Desktop preview.pyxpo-data/storage in the working folder

Quick start

app/index.py
from pyxpo import Button, Text, View
from pyxpo_storage import use_storage_number

def Counter():
    count, set_count = use_storage_number("launches")
    return View(
        Text("Saved count: %d" % (count or 0)),
        Button(title="Add one", onPress=lambda: set_count(lambda old: (old or 0) + 1)),
    )

default = Counter

Permissions

pyxpo adds these to Info.plist and AndroidManifest.xml for you when the module is listed.

iOSNone.
AndroidNone.

API reference

Create a store

Use the shared default store, or open separate stores by id. Two objects with the same id share data.

storage

The default store (id "default"). Ready to use after import.

Storage(config=None, *, id="default", path=None)→ Storage

Opens a named store. Also accepts Storage({"id": "user-42"}). The id has 1 to 120 letters, digits, '.', '_' or '-'. Passing encryptionKey raises ValueError: use pyxpo-secure-store for secrets.

store.path→ str

Path of the SQLite file.

store.size→ int

Bytes the store uses on disk.

Read and write (MMKV style)

set(key, value)

Stores a str, int, float, bool or bytes value.

get_string(key)→ str | None

The string under key, or None.

get_number(key)→ int | float | None

The number under key. An int stays an int.

get_boolean(key)→ bool | None

The boolean under key.

get_buffer(key)→ bytes | None

The bytes under key.

set_object(key, value) / get_object(key)

Stores and reads a JSON value such as a dict or list. get_object returns None when the key is missing or not valid JSON.

contains(key)→ bool

True when the key exists.

delete(key)

Removes one key.

get_all_keys()→ list[str]

Every key in the store.

clear_all()

Removes every key in the store.

trim()

Gives disk space of removed values back to the system.

add_on_value_changed_listener(fn)→ subscription

Calls fn(key) after a key is set or removed, from any Storage object on this store. Call .remove() on the result to stop.

AsyncStorage style

Same names as React Native AsyncStorage, but synchronous: do not await them.

get_item(key) / set_item(key, value) / remove_item(key)

String values only.

merge_item(key, value)

Deep-merges a JSON object string into the JSON object under key.

multi_get(keys)→ list[[key, value]]

Reads several keys at once.

multi_set(pairs) / multi_remove(keys) / multi_merge(pairs)

Batch versions of the calls above.

clear()

Removes every key.

Hooks

Each hook returns (value, set_value) and re-renders the component when the key changes anywhere in the app.

use_storage_string(key, instance=None)→ (str | None, setter)

Pass a Storage as instance to use a store other than the default.

use_storage_number(key, instance=None)→ (number | None, setter)
use_storage_boolean(key, instance=None)→ (bool | None, setter)
use_storage_buffer(key, instance=None)→ (bytes | None, setter)
use_storage_object(key, instance=None)→ (JSON value | None, setter)

For values saved with set_object.

use_storage_listener(fn, instance=None)

Calls fn(key) for every change while the component is mounted.

set_value(None)

Deletes the key. set_value(lambda old: ...) updates from the current value.

Examples

Remember a setting

A theme switch that survives restarts. The hook re-renders every screen that reads the same key.

python
from pyxpo import Switch, Text, View
from pyxpo_storage import use_storage_boolean

def Settings():
    dark, set_dark = use_storage_boolean("settings.dark")
    return View(
        Text("Dark mode"),
        Switch(value=bool(dark), onValueChange=set_dark),
    )

Store a JSON object

Keep small structured data such as a draft or a profile. Use a separate store id per user.

python
from pyxpo_storage import Storage

profile = Storage(id="user-42")
profile.set_object("profile", {"name": "Asha", "tags": ["admin"]})

data = profile.get_object("profile")   # {"name": "Asha", "tags": ["admin"]}
profile.contains("profile")            # True
profile.get_all_keys()                 # ["profile"]

Coming from AsyncStorage

The AsyncStorage names work, but they return at once. There is nothing to await.

python
import json
from pyxpo_storage import storage

storage.set_item("cart", json.dumps({"items": 2}))
storage.merge_item("cart", json.dumps({"coupon": "SAVE10"}))
storage.get_item("cart")   # '{"items":2,"coupon":"SAVE10"}'
storage.multi_get(["cart", "missing"])   # [["cart", "..."], ["missing", None]]

Good to know

  • Values are not encrypted. Keep tokens and passwords in pyxpo-secure-store.
  • The folder is private to the app and is removed when the app is uninstalled.
  • Set the PYXPO_STORAGE_DIR environment variable to move the folder, for example in tests.
  • camelCase names also work: getString, setItem, useStorageString and so on.