Skip to main content

State management library for Python (async RozRemembers + synchronous Store)

Project description

roz-remembers

A small, message-driven state management library for Python, inspired by the predictable state-container pattern popularized by Redux. It gives you a centralized, observable bag of state that you read and write through simple dot-path keys (e.g. "job.bins"), with change events you can subscribe to and optional JSON persistence.

It ships two front ends over the same dot-path engine:

  • Store — a synchronous store. Best for ordinary synchronous code that just wants central, observable runtime state without an event loop.
  • RozRemembers — an asyncio, Redux-style store driven by an action queue and an event queue. Best for long-running async applications.

Both share the exported helpers get_nested_value(path, data) and set_nested_value(path, value, data).

Installation

pip install roz-remembers

The import name is roz_remembers:

from roz_remembers import Store, RozRemembers, get_nested_value, set_nested_value

Dot-path convention

State is a nested dict, and every read/write addresses a value by a dot-separated path:

  • "user.theme"state["user"]["theme"]
  • numeric segments index into lists: "items.0.name"state["items"][0]["name"]
  • reads of a missing or non-traversable path return None (or your supplied default on Store.get).
  • writes auto-create intermediate dicts as needed. A write fails (returns False) only if a segment can't be traversed or assigned — e.g. a list index out of range, or trying to descend into a scalar.

Quickstart — Store (synchronous)

from roz_remembers import Store

store = Store({"job": {"bins": 10, "sorted": 0}})

# read / write by dot-path
store.set("job.sorted", 1)            # -> True
store.get("job.sorted")               # -> 1
store.get("job.missing", default=0)   # -> 0 (missing path falls back)

# writes auto-create intermediate dicts
store.set("machine.state", "CARD_READY")
store.get_state()                     # deep copy of the whole state

# subscribe to change events; subscribe() returns an unsubscribe function
def on_change(event):
    print(event["path"], event["old_value"], "->", event["new_value"])

unsubscribe = store.subscribe(on_change)
store.set("job.sorted", 2)            # -> on_change fires
unsubscribe()                         # stop receiving events

Persistence

Store reads and writes plain JSON:

store.save("state.json")                 # write current state to disk
restored = Store(state_file="state.json")  # load state at construction
restored.get("job.bins")                 # -> 10

# a Store created with state_file remembers it, so save() needs no argument
live = Store(state_file="state.json")
live.set("job.sorted", 5)
live.save()                              # persists back to state.json

Loading a missing file or malformed JSON starts from an empty state (a warning is logged) rather than raising, so a first run "just works".

Quickstart — RozRemembers (asynchronous)

RozRemembers processes actions off a queue and emits events onto another queue. You dispatch SET_STATE actions and consume STATE_CHANGED events.

import asyncio
from roz_remembers import RozRemembers

async def main():
    store = RozRemembers("initial_state.json")
    await store.load_initial_state()      # empty state if the file is absent
    store.start_processing()              # start the background action processor

    # observe state changes
    events = store.subscribe_events()     # an asyncio.Queue of event dicts

    await store.dispatch({
        "type": RozRemembers.ACTION_TYPE_SET_STATE,   # "SET_STATE"
        "path": "user.theme",
        "value": "dark",
    })

    event = await events.get()
    # {'type': 'STATE_CHANGED', 'path': 'user.theme',
    #  'old_value': None, 'new_value': 'dark', 'action_source': {...}}
    print(event["path"], "->", event["new_value"])
    print(store.get_current_state())      # deep copy of the whole state

    await store.stop_processing()

asyncio.run(main())

The subscribe / events model

  • Store notifies synchronously: each set() that changes state calls every subscriber callback with a STATE_CHANGED event ({"type", "path", "old_value", "new_value"}). A raising subscriber is logged and isolated — it won't break the store or other subscribers.
  • RozRemembers is asynchronous: subscribe_events() returns an asyncio.Queue. Each applied SET_STATE puts a STATE_CHANGED event (which also carries action_source) on that queue for your consumer coroutine to await. Actions with no path, unknown action types, and writes that can't be applied are ignored and emit no event.

Development

This is a Poetry (PEP 621) project. Tests run under pytest with a coverage gate:

pip install pytest pytest-asyncio pytest-cov
pytest        # runs the suite and enforces >=90% line coverage

pythonpath = ["src"] is set in pyproject.toml, so tests import roz_remembers directly without a manual PYTHONPATH.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

roz_remembers-0.2.0.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

roz_remembers-0.2.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file roz_remembers-0.2.0.tar.gz.

File metadata

  • Download URL: roz_remembers-0.2.0.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.04","id":"plucky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for roz_remembers-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4d667db39dcd6e5fadbf88398e2e552e0a382839211d652c9a7103d817896a44
MD5 06c5586e3d568f11bfd9be9fac0fde45
BLAKE2b-256 10fbad8bee5042c75e3b87cd9a9c7568ef258d764e72027a976b52e32cb307fe

See more details on using hashes here.

File details

Details for the file roz_remembers-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: roz_remembers-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.04","id":"plucky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for roz_remembers-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 13120f1cee4dd1d426d3509a8887140cb29f16d44f3d55f6a48f49bcd082b15e
MD5 123b8b57e6b132d5ebdc88c57f6b7af5
BLAKE2b-256 712e63a9d33d7f3960ff7ae1483591338808402552debc1984ae38a4ebea0fb9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page