Python state, React UI.
Project description
RemoteState - Python Library
remotestate is the Python runtime for RemoteState apps. It owns the state store, service methods, and server that expose Python state to React.
If you want the high-level product overview, start with the repository root README: RemoteState
Install
pip install remotestate
Or with pixi:
pixi add remotestate
Development
- Python
>= 3.12 - from the package directory:
cd remotestate-py - install dependencies with
pixi install - run tests with
pixi run tests - lint with
pixi run lint - format with
pixi run format - reorder imports with
pixi run isort - type-check with
pixi run mypy - build a wheel with
pixi run build
Quick Start
import remotestate as rs
class CounterService(rs.Service):
def __init__(self) -> None:
super().__init__(rs.Store({"count": 0, "user": {"name": "forman"}}))
@rs.action
async def increment(self) -> None:
self.store.set("count", self.store.get("count") + 1)
@rs.query
async def compute(self, x: float) -> float:
self.notify(name="Computing", detail="reading current count", progress=50)
return x * self.store.get("count")
rs.serve(CounterService(), ui_dist="my-ui/dist")
API Overview
The public Python API is exported from remotestate:
StoreServiceServeResultactionqueryservepath
Store
Store(initial, *, default_factory=None) holds the Python-side application state.
-
the root state may be any JSON-serializable value, including a mapping, list, scalar, Pydantic model, or dataclass
-
nested dicts, lists, Pydantic models, and dataclasses are all supported
-
default_factoryreceives the missing prefix as ars.path.Pathtuple -
statereturns the current root state value -
get(path=(), require=False)reads a value from a path such as ``,user.name, `[0].label`, or `items[0].label`; omit `path` to read the root state value -
set(path, value)writes a value and notifies subscribers -
store[path]andstore[path] = valueare notebook-friendly aliases forget()andset() -
store.at.some.path = valueis notebook-friendly sugar for nestedset()calls; use item syntax for keys that are not valid identifiers -
subscribe(callback)receives batched path-to-value updates after changes flush -
default_factorycan materialize missing parents while setting nested values
import remotestate as rs
class User:
def __init__(self, name: str = "", city: str = "") -> None:
self.name = name
self.city = city
def defaults(path: rs.path.Path):
if path == ("user",):
return User()
if path == ("items",):
return []
return {}
store = rs.Store({}, default_factory=defaults)
store.set("user.city", "Hamburg")
store.set("items[0].label", "foo")
store["items", 0, "label"] = "bar"
store.at.user.city = "Berlin"
store.at.items[0].label = "baz"
assert store.get("user.city") == "Berlin"
assert store.get() is store.state
assert store["items"] == [{"label": "baz"}]
assert store[()] is store.state
get() never calls the default factory. Reads stay side-effect free, and missing
values return None unless require=True is passed.
Actions and Queries
Use @action for state-changing service methods and @query for read-only methods.
@actionbatchesstore.set()calls and flushes them as oneaction_resultmessage.@queryis read-only; mutating the store inside a query raisesPermissionError- sync and async methods are both supported
class Counter(rs.Service):
def __init__(self) -> None:
super().__init__(rs.Store({"count": 0}))
@rs.action
def increment(self) -> None:
self.store.set("count", self.store.get("count") + 1)
@rs.query
async def multiply(self, x: float) -> float:
self.notify(name="Working", progress=25)
return x * self.store.get("count")
Service Helpers
Service exposes the store and progress helper used by actions and queries:
notify(name=None, detail=None, progress=None)emitsupdate_taskprogress messages for tracked calls
The reserved service method name is notify. Store reads and writes use
service.store.get(...) and service.store.set(...); get and set remain
available for custom actions or queries.
Service._init_app(app) can be overridden to customize the FastAPI app when serve()
creates one.
Serving
serve(service, *, ui_dist, mounts, app, display, width, height, host, port, **uvicorn_settings)
starts the RemoteState server and connects it to a frontend bundle.
serviceis aServiceinstanceui_distcan be a local React build directory or an HTTP(S) URLmountsadds additional static pathsapplets you supply your own FastAPI appdisplaycontrols how the UI is shown:"auto","browser","notebook","none", or a callbackhostandportconfigure the backend server
By default, RemoteState chooses a free port, opens a browser outside notebooks, and renders inline inside notebooks. Re-running the same notebook cell restarts the server automatically.
serve() returns a ServeResult with the resolved URLs and server handles:
result = rs.serve(CounterService(), ui_dist="my-ui/dist", display="none")
print("Server URL: ", result.server_url)
print("WebSocket URL: ", result.ws_url)
print("UI Base URL: ", result.ui_base_url)
Paths
remotestate.path exposes the parsed path types used by Store.default_factory and other
advanced integrations. Parsed paths are tuples of string property names and integer array
indices, matching the TypeScript package's string | number path segments:
PathPathSegmentPathInputPathSegmentInput
RemoteState paths use a simplified JSONPath
subset without the "$." prefix:
- the empty string addresses the root state value
- the first segment may be an identifier, bracketed integer index, or bracketed JSON string key
- later segments may be dotted identifiers, bracketed integer indices, or bracketed JSON string keys
- bracketed string keys may use either single or double quotes; canonical output uses double quotes
- the whole string must match the grammar; prefix parsing is not allowed
| Example | Valid? | Notes |
|---|---|---|
| empty string | yes | root state value |
user |
yes | root property shorthand |
[0].label |
yes | array root plus child property |
items[0].label |
yes | dotted identifier plus integer index |
["display name"].value |
yes | bracketed string key at the root |
user["display name"] |
yes | bracketed string key |
$.user |
no | "$." prefix is not part of the syntax |
items[01] |
no | indices are canonical integers without leading zeroes |
Use normalize_path() when accepting raw path inputs, and use parse_path() and
format_path() when you need to inspect, validate, or construct string paths.
More Docs
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file remotestate-0.3.2.tar.gz.
File metadata
- Download URL: remotestate-0.3.2.tar.gz
- Upload date:
- Size: 77.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
022f63d4d8db318fd3018c44a808c3c5b066240aa3f32c684fd392f3a314177a
|
|
| MD5 |
7133625d52da7d291720184b5a0d5b39
|
|
| BLAKE2b-256 |
6d5012a0bf2ada006f3620e459ea7a13f271767f5d38e927bfb58522c3db45d1
|
File details
Details for the file remotestate-0.3.2-py3-none-any.whl.
File metadata
- Download URL: remotestate-0.3.2-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afed2abfe3a68ddaea64b18d8280393f150fdedf5713c35d17b179d597bd1eb5
|
|
| MD5 |
c1ac8026a7dabd4e23f090be3b1a10f8
|
|
| BLAKE2b-256 |
ec54e861ada9f7604c1063476e9ad2505e4bd8a8133572e9902219e3d5bc69bf
|