Skip to main content

Bindable properties for Python

Project description

Binding

This package brings property binding to Python. It allows you to bind attributes of one object to other attributes of itself or other objects like so:

label.text.bind(model.value)

That means, whenever model.value is changed, label.text changes as well.

Installation

This package can be installed using the Python package installer pip:

python3 -m pip install binding

Alternatively you can find the source code on GitHub.

Usage

You can apply binding in two different ways: automatic updates using bindable properties or by calling an update functional explicitely. Furthermore, you can specify the binding direction as well as converter functions.

The following examples give a more detailed explanation. The code snippets build upon each other and are meant to be called in succession.

Bindable properties

If you have control over the class implementation, you can introduce a BindableProperty for the respective attributes. It will intercept each write access the attribute and propagate the changed value to bound properties:

from binding import BindableProperty

class Person:

    name = BindableProperty()

    def __init__(self, name=None):

        self.name = name

class Car:

    driver = BindableProperty()

    def __init__(self, driver=None):

        self.driver = driver

person = Person('Robert')
car = Car()
car.driver.bind(person.name)
assert car.driver == person.name == 'Robert'

person.name = 'Bob'
assert car.driver == person.name == 'Bob'

Binding with non-bindable attributes

Suppose you have a class which you cannot or don't want to change. That means it has no BindableProperty to observe value changes. You can bind its attributes nevertheless:

class License:

    def __init__(self, name=None):

        self.name = name

license = License()
license.name.bind(person.name)
person.name = 'Todd'
assert license.name == person.name == 'Todd'

But if the license name is changed, there is no BindableProperty to notice write access to its value. We have to manually trigger the propagation to bound objects.

from binding import update

license.name = 'Ben'
assert person.name != license.name == 'Ben'

update()
assert person.name == license.name == 'Ben'

One-way binding

The .bind() method registers two-way binding. But you can also specify one-way binding using .bind_from() or .bind_to(), respectively. In the following example car receives updates person, but not the other way around.

person = Person('Ken')
car = Car()

car.driver.bind_from(person.name)
assert car.driver == person.name == 'Ken'

person.name = 'Sam'
assert car.driver == person.name == 'Sam'

car.driver = 'Seth'
assert car.driver != person.name == 'Sam'

Likewise you can specify forward binding to let person be updated when car changes:

person = Person('Keith')
car = Car()

car.driver.bind_to(person.name)
assert car.driver == person.name == None

car.driver = 'Kent'
assert car.driver == person.name == 'Kent'

person.name = 'Grant'
assert car.driver != person.name == 'Grant'

Converters

For all types of binding - forward, backward, two-way, via bindable properties or non-bindable attributes - you can define converter functions that translate values from one side to another. The following example demonstrates the conversion between Celsius and Fahrenheit.

class Temperature:

    c = BindableProperty()
    f = BindableProperty()

    def __init__(self):

        self.c = 0.0
        self.f = 0.0

t = Temperature()
t.f.bind(t.c, forward=lambda f: (f - 32) / 1.8, backward=lambda c: c * 1.8 + 32)
assert t.c == 0.0 and t.f == 32.0

t.f = 68.0
assert t.c == 20.0 and t.f == 68.0

t.c = 100.0
assert t.c == 100.0 and t.f == 212.0

Note that bind_to() only needs a forward converter. Similarly bind_from has only a backward converter.

Implementation and dependencies

To achieve such a lean API we utilize three main techniques:

  • For extending basic types with bind(), bind_to() and bind_from() methods we use curse from the forbiddenfruit package.

  • For intercepting write access to attributes we implement BindableProperties as descriptors.

  • For finding the object and attribute name of the caller and the argument of our bind() methods we use inspection tools from the inspect and executing packages.

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

binding-0.2.tar.gz (5.0 kB view details)

Uploaded Source

Built Distribution

binding-0.2-py3-none-any.whl (4.8 kB view details)

Uploaded Python 3

File details

Details for the file binding-0.2.tar.gz.

File metadata

  • Download URL: binding-0.2.tar.gz
  • Upload date:
  • Size: 5.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1047-azure

File hashes

Hashes for binding-0.2.tar.gz
Algorithm Hash digest
SHA256 329b7ab019d06f86269e1e80dac3eeb3e8b7b1f52c790c73197c31c82e1b4e74
MD5 f45e2de8ee2572cafb3b6975a39ff61b
BLAKE2b-256 98660133e78a8485a6ac7284badcaebe93a37f239ff19260c8d45e6ee2c0b5af

See more details on using hashes here.

File details

Details for the file binding-0.2-py3-none-any.whl.

File metadata

  • Download URL: binding-0.2-py3-none-any.whl
  • Upload date:
  • Size: 4.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1047-azure

File hashes

Hashes for binding-0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8f90492df742a8398a9e9cf727d562da3f4ff840a3bf4016f75896782fe87f02
MD5 fe13db41a780da15bbfee70d75cf0b1b
BLAKE2b-256 b0fde90a6a0cab5b459a005687a0f27c69438f858680e8541ca83bf9ce28d8af

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page