A library which provides observable values to which other values can be bound
Project description
spellbind
Reactive programming for Python with reactive variables and events.
spellbind is a reactive programming library that lets you create Variables that automatically update when their dependencies change, plus an event system for notifying observers.
Installation
pip install spellbind
Quick Start
from spellbind.int_values import IntVariable
from spellbind.str_values import StrVariable
# Create reactive variables
name = StrVariable("Alice")
age = IntVariable(25)
# Create computed values that automatically update
greeting = name + " is " + age.to_str() + " years old"
print(greeting) # "Alice is 25 years old"
# Update source values - computed values update automatically!
name.value = "Bob"
age.value = 30
print(greeting) # "Bob is 30 years old"
Core Concepts
Values, Variables and Events
The foundation of spellbind consists of three key components:
Values are read-only reactive data that can be observed for changes. Variables are mutable Values that can be changed and bound to other Values. Events provide a way to notify observers when something happens.
from spellbind.values import Constant
from spellbind.int_values import IntVariable
from spellbind.event import Event
# Variables can be changed
counter = IntVariable(0)
counter.value = 10
# Constants cannot be changed
pi = Constant(3.14159)
# Events notify observers
button_clicked = Event()
button_clicked.observe(lambda: print("Clicked!"))
button_clicked() # Prints: "Clicked!"
Reactive Bindings
Variables can be bound to other Values, making them automatically update:
from spellbind.int_values import IntVariable
# Create computed values
base = IntVariable(10)
multiplier = IntVariable(3)
result = base * multiplier
# Bind variables to computed values
my_variable = IntVariable(0)
my_variable.bind_to(result)
print(my_variable) # 30
# Updates propagate automatically
base.value = 20
print(my_variable) # 60
# Unbind to break connections
my_variable.unbind()
Advanced Features
Weak vs Strong Binding
Control memory management with binding strength:
from spellbind.str_values import StrVariable
source = StrVariable("hello")
target = StrVariable("")
# Strong binding (default) - keeps source alive
target.bind_to(source, bind_weakly=False)
# Weak binding - allows source to be garbage collected
target.bind_to(source, bind_weakly=True)
Circular Dependency Detection
spellbind automatically prevents circular dependencies:
from spellbind.int_values import IntVariable
a = IntVariable(1)
b = IntVariable(2)
a.bind_to(b)
# b.bind_to(a) # This would raise RecursionError
Observing Changes
React to value changes with observers:
from spellbind.int_values import IntVariable
def on_value_change(new_value):
print(f"Value changed to: {new_value}")
my_var = IntVariable(42)
my_var.observe(on_value_change)
my_var.value = 100 # Prints: "Value changed to: 100"
Event System
spellbind includes an event system for notifying observers when things happen.
Basic Events
from spellbind.event import Event
# Create an event
button_clicked = Event()
# Add observers
def handle_click():
print("Button was clicked!")
button_clicked.observe(handle_click)
# Trigger the event
button_clicked() # Prints: "Button was clicked!"
Value Events
Events that pass data to observers:
from spellbind.event import ValueEvent
user_logged_in = ValueEvent[str]()
def welcome_user(username: str):
print(f"Welcome, {username}!")
user_logged_in.observe(welcome_user)
user_logged_in("Alice") # Prints: "Welcome, Alice!"
Multi-Parameter Events
Events with multiple parameters:
from spellbind.event import BiEvent, TriEvent
# Two parameters
position_changed = BiEvent[int, int]()
position_changed.observe(lambda x, y: print(f"Position: ({x}, {y})"))
position_changed(10, 20) # Prints: "Position: (10, 20)"
# Three parameters
rgb_changed = TriEvent[int, int, int]()
rgb_changed.observe(lambda r, g, b: print(f"Color: rgb({r}, {g}, {b})"))
rgb_changed(255, 128, 0) # Prints: "Color: rgb(255, 128, 0)"
Weak Observation
Prevent memory leaks with weak observers:
from spellbind.event import Event
event = Event()
def temporary_handler():
print("Handling event")
# Weak observation - handler can be garbage collected
event.weak_observe(temporary_handler)
Example Application
Here's a practical example showing how to create automatically positioned windows:
from spellbind.int_values import IntVariable
class Window:
def __init__(self, x: int, y: int, width: int, height: int):
self.x = IntVariable(x)
self.y = IntVariable(y)
self.width = IntVariable(width)
self.height = IntVariable(height)
def __repr__(self):
return f"Window(x={self.x.value}, y={self.y.value}, width={self.width.value}, height={self.height.value})"
# Create two windows
main_window = Window(100, 50, 800, 600)
sidebar_window = Window(0, 0, 200, 400)
# Automatically position sidebar to the right of main window
margin = IntVariable(10)
sidebar_window.x.bind_to(main_window.x + main_window.width + margin)
sidebar_window.y.bind_to(main_window.y)
print(main_window) # Window(x=100, y=50, width=800, height=600)
print(sidebar_window) # Window(x=910, y=50, width=200, height=400)
# Moving the main window automatically repositions the sidebar
main_window.x.value = 200
main_window.y.value = 100
print(main_window) # Window(x=200, y=100, width=800, height=600)
print(sidebar_window) # Window(x=1010, y=100, width=200, height=400)
# Changing margin updates sidebar position
margin.value = 20
print(sidebar_window) # Window(x=1020, y=100, width=200, height=400)
API Reference
Core Classes
Value[T]- Type for all reactive values, useful for typing function parametersVariable[T]- Type for mutable values, useful for typing function parametersConstant[T]- Immutable value
Type-Specific Classes
IntValue,IntVariable- Integer values with arithmetic operationsFloatValue,FloatVariable- Float values with arithmetic operationsStrValue,StrVariable- String values with concatenationBoolValue- Boolean values with logical operations
Event Classes
Event- Basic event with no parametersValueEvent[T]- Event that passes one valueBiEvent[S, T]- Event that passes two valuesTriEvent[S, T, U]- Event that passes three values
Development
Running Tests
pytest
Type Checking
mypy src
Linting
flake8 .
Author: Georg Plaz
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
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 spellbind-0.4.1.tar.gz.
File metadata
- Download URL: spellbind-0.4.1.tar.gz
- Upload date:
- Size: 65.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
914a0c781cbf95687e31cea0214f25c912a061e88e0c4612f8f4e8dc921b621b
|
|
| MD5 |
fa3116166ce96da82e5e64594577ba50
|
|
| BLAKE2b-256 |
07de105c45adadc2eda522ea32f75bbdd5c476d91086ae5b445ea39143b3a444
|
Provenance
The following attestation bundles were made for spellbind-0.4.1.tar.gz:
Publisher:
python-package.yml on FancyNeuron/spellbind
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spellbind-0.4.1.tar.gz -
Subject digest:
914a0c781cbf95687e31cea0214f25c912a061e88e0c4612f8f4e8dc921b621b - Sigstore transparency entry: 309207252
- Sigstore integration time:
-
Permalink:
FancyNeuron/spellbind@b6e48311da371f76b03e3b5f9fa423668bf4729a -
Branch / Tag:
refs/tags/0.4.1 - Owner: https://github.com/FancyNeuron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-package.yml@b6e48311da371f76b03e3b5f9fa423668bf4729a -
Trigger Event:
release
-
Statement type:
File details
Details for the file spellbind-0.4.1-py3-none-any.whl.
File metadata
- Download URL: spellbind-0.4.1-py3-none-any.whl
- Upload date:
- Size: 34.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c2cdce265df093f88a81ed35450279b6447dda2dd739e9eebebb777f9103f6f
|
|
| MD5 |
928f2644f08d5084396375c05d8f045e
|
|
| BLAKE2b-256 |
c7955747471c9d1d68b5cc59b4672dc59fd807b884d3854eec1a50a6b7f3c40c
|
Provenance
The following attestation bundles were made for spellbind-0.4.1-py3-none-any.whl:
Publisher:
python-package.yml on FancyNeuron/spellbind
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spellbind-0.4.1-py3-none-any.whl -
Subject digest:
0c2cdce265df093f88a81ed35450279b6447dda2dd739e9eebebb777f9103f6f - Sigstore transparency entry: 309207266
- Sigstore integration time:
-
Permalink:
FancyNeuron/spellbind@b6e48311da371f76b03e3b5f9fa423668bf4729a -
Branch / Tag:
refs/tags/0.4.1 - Owner: https://github.com/FancyNeuron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-package.yml@b6e48311da371f76b03e3b5f9fa423668bf4729a -
Trigger Event:
release
-
Statement type: