Skip to main content

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.

Metadata

Release files for binding 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for binding 0.3.1
File Size Uploaded Provenance
binding-0.3.1.tar.gz 5.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for binding 0.3.1
File Interpreter ABI Platform
binding-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 10.0 kB

Release files / binding-0.3.1.tar.gz

Download URL binding-0.3.1.tar.gz
Size 5.1 kB
Tags Source
SHA-256 checksum
How to use checksums
66bc5c465369ccc35c0d587865f1c9a9a8abc41cfa275f4adc748ae42dcc0d7f
BLAKE2b-256 checksum
How to use checksums
b25c7cc4fad8448e70aee0a5e6943658dbf8f086987d8f2dc260b87e1439243e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.6 CPython/3.9.5 Linux/5.8.0-1036-azure

Release files / binding-0.3.1-py3-none-any.whl

Download URL binding-0.3.1-py3-none-any.whl
Size 4.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1bd36c3c1abb388fb15bd6967d340a7025e23b54b622ca9a6e43d4c1caf14b55
BLAKE2b-256 checksum
How to use checksums
2dd09e4a53d68388da4088dc7b8ae7bfc0e4ba59a460d59cfef4fdd39351394d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.6 CPython/3.9.5 Linux/5.8.0-1036-azure

Security for binding 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page