Skip to main content

Make your code reactive; Turn plain JSONs into a live data store, two-way synced with Python objects.

Project description

Syncwave

Make your code reactive; Turn plain JSONs into a live data store, two-way synced with Python objects.

⚠️ Warning

Syncwave is under active development. Until version 1.0, any minor release (0.x) may introduce breaking changes. Pin the exact version in production.

Version 1.0 will be released when the library is stable, feature-complete, and tested.

Installation

Install from PyPI.

# pip
pip install syncwave

# uv
uv add syncwave

Requires Python 3.9+.

Quick Start

Bind in-memory data to a JSON file with @syncwave.register. Syncwave automatically synchronizes the file with your in-memory objects.

from pydantic import BaseModel

from syncwave import Syncwave

syncwave = Syncwave()


# Register a model — creates `syncstores/customers.json` automatically.
# If the file already exists, its data is loaded into the store.
@syncwave.register(name="customers")
class Customer(BaseModel):
    key: int
    name: str
    age: int


customers = syncwave["customers"]

# Add entries — dicts and model instances both work.
customers[1] = {"key": 1, "name": "John Doe", "age": 30}
customers[2] = Customer(key=2, name="Jane Doe", age=25)

# Grab a reference and mutate — changes are written to disk automatically.
john = customers[1]
john.age = 31  # syncstores/customers.json is updated

# The store is a dict-like object that can be printed.
print(customers)  # {1: key=1 name='John Doe' age=31, 2: key=2 name='Jane Doe' age=25}
print(syncwave)  # {'customers': ...}

# Delete an entry.
del customers[1]  # removed from memory and from the file

# External edits propagate back.
input("Go edit syncstores/customers.json, then press Enter... ")
print(customers)  # reflects the external changes

The Reactive System

See docs/reactivity.md.

API Reference

Syncwave

The entry point. A MutableMapping[str, Any] where each key is a store name and each value is the store's data (which may or may not be reactive). Each store maps to a single JSON file.

from syncwave import Syncwave

syncwave = Syncwave(root_path="data")
  • root_path (optional): Directory for JSON files. Defaults to ./syncstores.
  • syncwave.root_path: Read-only property returning the resolved path.

Methods:

  • syncwave.create_store(tp, *, name, default) — Creates a store for a given type. The type can be anything supported by Pydantic, including reactive types. Creates or loads the associated JSON file. A default is required when the file is empty and the type has no natural empty form (e.g., int).
  • syncwave.make_reactive(cls, *, cls_name) — Turns a Pydantic model into a SyncModel class.
  • @syncwave.register(*, name, collection) — Class decorator that makes a model reactive and creates a store for it in one step. See Collection Wrapping below.

Standard MutableMapping operations are supported: syncwave[name], syncwave[name] = value, del syncwave[name], len(syncwave), iteration, etc.

SyncDict, SyncList, SyncSet

Reactive counterparts of dict, list, and set. They behave like their standard equivalents (MutableMapping, MutableSequence, MutableSet) but every mutation is validated by Pydantic, persisted to disk, and propagated from disk.

They are parameterized like their standard counterparts:

from syncwave import SyncDict, SyncList, SyncSet

syncwave.create_store(SyncDict[str, int], name="settings")
syncwave.create_store(SyncList[str], name="tags")
syncwave.create_store(SyncSet[int], name="ids")

Reactive collections can be nested: SyncDict[str, SyncList[int]], SyncList[SyncDict[str, str]], etc. SyncSet cannot hold reactive items (since reactive objects are mutable and therefore not hashable).

SyncCollection is the abstract base for all three — useful for isinstance checks.

SyncModel

A user-defined Pydantic model that Syncwave has made reactive. Attribute access and assignment are intercepted to keep the data in sync.

Created via syncwave.make_reactive or @syncwave.register. The supported model types are:

  1. subclasses of pydantic.BaseModel,
  2. subclasses of pydantic.RootModel,
  3. classes decorated with @pydantic.dataclasses.dataclass.

@syncwave.register returns the original class, not the SyncModel. You can keep using it normally — Syncwave handles the reactive wrapping internally.

Reactive

Abstract base class that all reactive types inherit from. Provides the sync_live property, which is True while the object is connected to a store and False after it has been killed (e.g., by deleting the store or replacing the entry). Operations on a dead reference raise DeadReferenceError.

from syncwave import Reactive

ref = syncwave["customers"][1]
isinstance(ref, Reactive)  # True
ref.sync_live               # True

del syncwave["customers"][1]
ref.sync_live               # False

is_sync_model_supported

Type guard that returns True if a class can be made reactive (i.e., it is a BaseModel, RootModel, or Pydantic dataclass, and is not already a SyncModel).

from syncwave import is_sync_model_supported

is_sync_model_supported(Customer)  # True
is_sync_model_supported(int)       # False

Collection Wrapping

When using @syncwave.register, the collection parameter controls how the model is stored:

Model shape Wrapping Access pattern
Has a key field SyncDict[<key type>, Model] store[key]
No key field SyncList[Model] store[index]
Subclass of RootModel No wrapping syncwave["name"] directly

This is the default collection="auto" behavior. You can override it:

from syncwave import SyncList


# Force a list even though the model has a `key` field.
@syncwave.register(name="items", collection=SyncList)
class Item(BaseModel):
    key: int  # ignored
    name: str


# No wrapping — the store holds a single model instance.
@syncwave.register(name="config", collection=None)
class Config(BaseModel):
    theme: str
    debug: bool

License

Syncwave is licensed under the MIT License.

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

syncwave-0.2.1.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

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

syncwave-0.2.1-py3-none-any.whl (26.2 kB view details)

Uploaded Python 3

File details

Details for the file syncwave-0.2.1.tar.gz.

File metadata

  • Download URL: syncwave-0.2.1.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for syncwave-0.2.1.tar.gz
Algorithm Hash digest
SHA256 b1cbbc3bb4fe9a93ff1973372c38f0be668811ff394f5a4ab18ee576737261b6
MD5 e539a3cd95e566241ced72b80b15accd
BLAKE2b-256 6ca9b0ff06d75bc73a836b0eb8cdf57f8d05e4f96a31c8686efbf539d51a4316

See more details on using hashes here.

Provenance

The following attestation bundles were made for syncwave-0.2.1.tar.gz:

Publisher: release.yml on iannitello/syncwave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file syncwave-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: syncwave-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 26.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for syncwave-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5f15103dab293b4ca516b1495f3ea574a951bdced228849e361b80d6db6a4347
MD5 777d3dd599a640c532f2a23355bbd07c
BLAKE2b-256 11da460bf31de42f61fd2cc57269c3c316a1efbe29d5f50a420a01e5ac55510a

See more details on using hashes here.

Provenance

The following attestation bundles were made for syncwave-0.2.1-py3-none-any.whl:

Publisher: release.yml on iannitello/syncwave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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