Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

180 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Observ ๐Ÿ‘

Vue.js-style reactivity for Python. Wrap your state in a reactive proxy, and observ tracks every read and write for you: computed values invalidate themselves, watchers fire when something they depend on changes. No dirty flags, no manual invalidation, no dependency bookkeeping.

๐Ÿ“– Documentation ยท ๐Ÿ“ฆ PyPI ยท ๐Ÿงช Examples (Qt, rendercanvas)

๐Ÿ–ผ๏ธ Building a GUI?

Head straight to Collagraph โ€” a full-featured, Vue-inspired declarative UI framework built on top of observ. If you're looking for a strong reactive GUI framework for your Python Qt/PySide applications, Collagraph is the main entrypoint; observ is the reactivity engine underneath it.

Why observ?

  • Automatic dependency tracking โ€” computed state knows exactly what it depends on and lazily re-evaluates only when needed.
  • React to any change โ€” watch plain state or computed state and build unidirectional data flow: state changes drive view changes, input events drive state changes.
  • Zero dependencies, framework agnostic โ€” a pure Python library (โ‰ฅ 3.13) that plugs into any event loop: asyncio, Qt, or your own.
  • Fully typed โ€” a PEP 561 typed package, checked with ty in CI.

Quick start

pip install observ
from observ import computed, reactive, watch

state = reactive({
    "todos": [
        {"title": "groceries", "done": False},
        {"title": "dishes", "done": True},
    ],
})

@computed
def progress():
    done = sum(todo["done"] for todo in state["todos"])
    return f"{done}/{len(state['todos'])} done"

watcher = watch(progress, lambda new: print(new), sync=True)

# Mutate the plain dicts and lists you already have โ€”
# observ sees every change, no matter how deeply nested:
state["todos"][0]["done"] = True                            # prints: 2/2 done
state["todos"].append({"title": "laundry", "done": False})  # prints: 2/3 done

# ...but you only ever react when a *result* actually changes:
state["todos"][1]["title"] = "do the dishes"                # (no print)

No subclasses to inherit, no observable fields to declare, no signals to wire up: reactive() takes your existing data, and dependencies are tracked automatically simply by using it.

Continue with the Quick Start tutorial, or dive into the Guide and API Reference.

Related projects

  • Collagraph: reactive user interfaces in Python, built on top of observ.
  • Reliev: the store (with undo/redo support) that used to be part of observ.
  • Patchdiff: diffing and patching for plain Python data structures โ€” the same dicts, lists and sets you'd wrap with observ.

About

Observ ๐Ÿ‘ Reactive state management

Resources

Stars

Watchers

Forks

Releases

Used by

Contributors

Languages