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.
Add storage to modules in your app config, then import it in your code.
[tool.pyxpo]
modules = ["storage"]from pyxpo_storage import storage, use_storage_stringThis module is pure Python. It still goes in modules so the build includes it in your app.
Platforms
Quick start
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 = CounterPermissions
pyxpo adds these to Info.plist and AndroidManifest.xml for you when the module is listed.
| iOS | None. |
|---|---|
| Android | None. |
API reference
Create a store
Use the shared default store, or open separate stores by id. Two objects with the same id share data.
storageThe default store (id "default"). Ready to use after import.
Storage(config=None, *, id="default", path=None)→ StorageOpens 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→ strPath of the SQLite file.
store.size→ intBytes 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 | NoneThe string under key, or None.
get_number(key)→ int | float | NoneThe number under key. An int stays an int.
get_boolean(key)→ bool | NoneThe boolean under key.
get_buffer(key)→ bytes | NoneThe 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)→ boolTrue 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)→ subscriptionCalls 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.
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.
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.
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.