Skip to main content

A library which provides observable values to which other values can be bound

Project description

spellbind

Python 3.10+ Tests

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

git clone https://github.com/FancyNeuron/spellbind.git
cd spellbind
pip install -e .

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 parameters
  • Variable[T] - Type for mutable values, useful for typing function parameters
  • Constant[T] - Immutable value

Type-Specific Classes

  • IntValue, IntVariable - Integer values with arithmetic operations
  • FloatValue, FloatVariable - Float values with arithmetic operations
  • StrValue, StrVariable - String values with concatenation
  • BoolValue - Boolean values with logical operations

Event Classes

  • Event - Basic event with no parameters
  • ValueEvent[T] - Event that passes one value
  • BiEvent[S, T] - Event that passes two values
  • TriEvent[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

spellbind-0.2.0.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

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

spellbind-0.2.0-py3-none-any.whl (13.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: spellbind-0.2.0.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for spellbind-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e4ef780f3b51a2daf6af537d06b4abc8e3ea9b7205a68427e156d96516acb126
MD5 b1a1c76b47b34318e969a5210a588d58
BLAKE2b-256 1f4e296fcf4f8bec27384fd9117d2ed4a8b1dd1692ef344fe265cb96be137426

See more details on using hashes here.

Provenance

The following attestation bundles were made for spellbind-0.2.0.tar.gz:

Publisher: python-package.yml on FancyNeuron/spellbind

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

File details

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

File metadata

  • Download URL: spellbind-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 13.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for spellbind-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 703cf8f24d22adfe8b49c2693832d4dc61452b51580ec2f0cb30ce14cdf3f1a2
MD5 56963d0f23e865ecfb6faa80b5b8b002
BLAKE2b-256 12c4f6702fe0a5fce305a71db3b78b9b1cbd8a7b075dc7386b2bb5564a4a1477

See more details on using hashes here.

Provenance

The following attestation bundles were made for spellbind-0.2.0-py3-none-any.whl:

Publisher: python-package.yml on FancyNeuron/spellbind

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