Preferences Store#
Store is a small, file-backed key-value store for the bits of state an app
wants to remember between runs — the chosen theme, a “show tips on startup”
flag, recent files, the last-opened tab. It behaves like a dictionary, persists
to a JSON file under the OS configuration directory, and saves on every change.
It is deliberately not a data source: there are no rows, ids, pagination, or
queries. Use Data Sources for record-oriented data, and
Store for “remember this setting”.
Creating a store#
Give the store a logical name. Its file lives under the per-platform config
directory (Library/Application Support on macOS, %APPDATA% on Windows,
$XDG_CONFIG_HOME on Linux), in a sub-directory named for your app:
from bootstack.store import Store
import bootstack as bs
store = Store("settings") # <config>/<app>/settings.json
Pass path= for an explicit location, or app_name= to override the
sub-directory. A Store does not need a running App — it is plain file
I/O — so you can create one before constructing the app (handy for restoring the
startup theme).
Reading and writing#
Read with get() (with an optional default) and write with set(). The
mapping syntax works too:
store.set("theme", "bootstrap-dark")
store.get("theme", "bootstrap-light") # default if absent
store["sidebar_open"] = True
store["sidebar_open"] # KeyError if absent
"theme" in store
del store["theme"] # KeyError if absent
store.delete("theme") # no-op if absent
By default every change is written immediately, with an atomic write (a
temporary file renamed over the target) so a crash mid-write cannot corrupt the
file. Values must be JSON-serializable — scalars, lists, and dicts; anything
else raises SerializationError. Keys must be strings.
store.set("recent_files", ["a.txt", "b.txt"]) # lists/dicts are fine
store.set("handle", open("x")) # SerializationError
Bulk and dict-like operations#
Store supports the familiar mapping methods; update() writes once at the
end rather than per key, and accepts a mapping, keyword arguments, or both:
store.update({"theme": "dark", "font_scale": 1.2})
store.update(theme="dark", locale="de_DE") # keyword form
store.setdefault("locale", "en_US")
store.keys(); store.values(); store.items()
len(store); list(store)
store.as_dict() # a plain-dict copy
store.clear()
Deferring writes#
Pass autosave=False to batch changes in memory and flush them yourself with
save() — useful when applying many settings at once:
store = Store("settings", autosave=False)
store.update(collected_settings)
store.save()
Call reload() to discard in-memory state and re-read the file (for example
if another process may have written it).
Remembering the theme#
A common use is restoring the user’s theme across launches — read it before creating the app, and write it whenever it changes:
store = Store("settings")
with bs.App(theme=store.get("theme", "bootstrap-light")) as app:
app.on_theme_change(lambda theme: store.update(theme=theme))
bs.Button("Toggle theme", on_click=bs.toggle_theme)
app.run()
Persisting app configuration#
Because app configuration is flat App(...) kwargs and a Store is a flat
dict, the whole persistence story is symmetric: splat the store in to restore,
and write each value back when it changes. App.from_store() does the restore
and tolerantly ignores keys that are not valid configuration, so a settings
file written by an older or newer version (with renamed or removed keys) still
loads cleanly instead of raising:
store = Store("settings") # app config, in its own store
app = bs.App.from_store(store) # restore; empty store → defaults
app.on_theme_change(lambda theme: store.update(theme=theme))
app.on_locale_change(lambda locale: store.update(locale=locale))
app.run()
A few practices keep this clean:
Keep app *config* separate from app *state*. Put restorable configuration (theme, locale) in one store and transient state (recent files, last-opened tab) in another, so
App.from_storeonly ever sees real config keys.Window geometry is the one exception. It is a fiddly
"WxH+X+Y"string updated continuously as the window moves, not a clean kwarg — let the built-inremember_window_state=Trueflag handle it rather than the store.Per-value write-back, not a config dump. React to the specific change events (
on_theme_change/on_locale_change) and write just that key.
See also#
Data Sources — record-oriented data with querying and paging.
Theming — themes and the values worth persisting with a store.
Errors —
SerializationErrorand the other public errors.
API reference#
The complete reference for Store — every mapping
method — lives in Storage. At a glance:
A persistent, dict-like key-value store for app preferences. |