binding


Namebinding JSON
Version 0.3.1 PyPI version JSON
download
home_pagehttps://github.com/zauberzeug/binding
SummaryBindable properties for Python
upload_time2021-07-15 08:19:51
maintainer
docs_urlNone
authorZauberzeug GmbH
requires_python>=3.7,<4.0
licenseMIT
keywords bind properties ui automatic values
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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:

```Python
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](https://pypi.org/project/pip/):

```bash
python3 -m pip install binding
```

Alternatively you can find the [source code](https://github.com/zauberzeug/binding) 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:

```python
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:

```python
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.

```python
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.

```python
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:

```python
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.

```python
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](https://pypi.org/project/forbiddenfruit/) package.

- For intercepting write access to attributes we implement `BindableProperties` as [descriptors](https://docs.python.org/3/howto/descriptor.html).

- For finding the object and attribute name of the caller and the argument of our `bind()` methods we use inspection tools from the [inspect](https://docs.python.org/3/library/inspect.html) and [executing](https://pypi.org/project/executing/) packages.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/zauberzeug/binding",
    "name": "binding",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7,<4.0",
    "maintainer_email": "",
    "keywords": "bind,properties,ui,automatic,values",
    "author": "Zauberzeug GmbH",
    "author_email": "info@zauberzeug.com",
    "download_url": "https://files.pythonhosted.org/packages/b2/5c/7cc4fad8448e70aee0a5e6943658dbf8f086987d8f2dc260b87e1439243e/binding-0.3.1.tar.gz",
    "platform": "",
    "description": "# Binding\n\nThis package brings property binding to Python.\nIt allows you to bind attributes of one object to other attributes of itself or other objects like so:\n\n```Python\nlabel.text.bind(model.value)\n```\n\nThat means, whenever `model.value` is changed, `label.text` changes as well.\n\n# Installation\n\nThis package can be installed using the Python package installer [pip](https://pypi.org/project/pip/):\n\n```bash\npython3 -m pip install binding\n```\n\nAlternatively you can find the [source code](https://github.com/zauberzeug/binding) on GitHub.\n\n# Usage\n\nYou can apply binding in two different ways: automatic updates using bindable properties or by calling an update functional explicitely.\nFurthermore, you can specify the binding direction as well as converter functions.\n\nThe following examples give a more detailed explanation.\nThe code snippets build upon each other and are meant to be called in succession.\n\n## Bindable properties\n\nIf 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:\n\n```python\nfrom binding import BindableProperty\n\nclass Person:\n\n    name = BindableProperty()\n\n    def __init__(self, name=None):\n\n        self.name = name\n\nclass Car:\n\n    driver = BindableProperty()\n\n    def __init__(self, driver=None):\n\n        self.driver = driver\n\nperson = Person('Robert')\ncar = Car()\ncar.driver.bind(person.name)\nassert car.driver == person.name == 'Robert'\n\nperson.name = 'Bob'\nassert car.driver == person.name == 'Bob'\n```\n\n## Binding with non-bindable attributes\n\nSuppose you have a class which you cannot or don't want to change.\nThat means it has no `BindableProperty` to observe value changes.\nYou can bind its attributes nevertheless:\n\n```python\nclass License:\n\n    def __init__(self, name=None):\n\n        self.name = name\n\nlicense = License()\nlicense.name.bind(person.name)\nperson.name = 'Todd'\nassert license.name == person.name == 'Todd'\n```\n\nBut if the license name is changed, there is no `BindableProperty` to notice write access to its value.\nWe have to manually trigger the propagation to bound objects.\n\n```python\nfrom binding import update\n\nlicense.name = 'Ben'\nassert person.name != license.name == 'Ben'\n\nupdate()\nassert person.name == license.name == 'Ben'\n```\n\n## One-way binding\n\nThe `.bind()` method registers two-way binding.\nBut you can also specify one-way binding using `.bind_from()` or `.bind_to()`, respectively.\nIn the following example `car` receives updates `person`, but not the other way around.\n\n```python\nperson = Person('Ken')\ncar = Car()\n\ncar.driver.bind_from(person.name)\nassert car.driver == person.name == 'Ken'\n\nperson.name = 'Sam'\nassert car.driver == person.name == 'Sam'\n\ncar.driver = 'Seth'\nassert car.driver != person.name == 'Sam'\n```\n\nLikewise you can specify forward binding to let `person` be updated when `car` changes:\n\n```python\nperson = Person('Keith')\ncar = Car()\n\ncar.driver.bind_to(person.name)\nassert car.driver == person.name == None\n\ncar.driver = 'Kent'\nassert car.driver == person.name == 'Kent'\n\nperson.name = 'Grant'\nassert car.driver != person.name == 'Grant'\n```\n\n## Converters\n\nFor 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.\nThe following example demonstrates the conversion between Celsius and Fahrenheit.\n\n```python\nclass Temperature:\n\n    c = BindableProperty()\n    f = BindableProperty()\n\n    def __init__(self):\n\n        self.c = 0.0\n        self.f = 0.0\n\nt = Temperature()\nt.f.bind(t.c, forward=lambda f: (f - 32) / 1.8, backward=lambda c: c * 1.8 + 32)\nassert t.c == 0.0 and t.f == 32.0\n\nt.f = 68.0\nassert t.c == 20.0 and t.f == 68.0\n\nt.c = 100.0\nassert t.c == 100.0 and t.f == 212.0\n```\n\nNote that `bind_to()` only needs a `forward` converter.\nSimilarly `bind_from` has only a `backward` converter.\n\n# Implementation and dependencies\n\nTo achieve such a lean API we utilize three main techniques:\n\n- For extending basic types with `bind()`, `bind_to()` and `bind_from()` methods we use `curse` from the [forbiddenfruit](https://pypi.org/project/forbiddenfruit/) package.\n\n- For intercepting write access to attributes we implement `BindableProperties` as [descriptors](https://docs.python.org/3/howto/descriptor.html).\n\n- For finding the object and attribute name of the caller and the argument of our `bind()` methods we use inspection tools from the [inspect](https://docs.python.org/3/library/inspect.html) and [executing](https://pypi.org/project/executing/) packages.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Bindable properties for Python",
    "version": "0.3.1",
    "project_urls": {
        "Homepage": "https://github.com/zauberzeug/binding",
        "Repository": "https://github.com/zauberzeug/binding"
    },
    "split_keywords": [
        "bind",
        "properties",
        "ui",
        "automatic",
        "values"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2dd09e4a53d68388da4088dc7b8ae7bfc0e4ba59a460d59cfef4fdd39351394d",
                "md5": "e78795406c42cfa63ffd35268d55392d",
                "sha256": "1bd36c3c1abb388fb15bd6967d340a7025e23b54b622ca9a6e43d4c1caf14b55"
            },
            "downloads": -1,
            "filename": "binding-0.3.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e78795406c42cfa63ffd35268d55392d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7,<4.0",
            "size": 4885,
            "upload_time": "2021-07-15T08:19:49",
            "upload_time_iso_8601": "2021-07-15T08:19:49.984824Z",
            "url": "https://files.pythonhosted.org/packages/2d/d0/9e4a53d68388da4088dc7b8ae7bfc0e4ba59a460d59cfef4fdd39351394d/binding-0.3.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b25c7cc4fad8448e70aee0a5e6943658dbf8f086987d8f2dc260b87e1439243e",
                "md5": "22162008916d2bfd597854ca18532a27",
                "sha256": "66bc5c465369ccc35c0d587865f1c9a9a8abc41cfa275f4adc748ae42dcc0d7f"
            },
            "downloads": -1,
            "filename": "binding-0.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "22162008916d2bfd597854ca18532a27",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7,<4.0",
            "size": 5078,
            "upload_time": "2021-07-15T08:19:51",
            "upload_time_iso_8601": "2021-07-15T08:19:51.528212Z",
            "url": "https://files.pythonhosted.org/packages/b2/5c/7cc4fad8448e70aee0a5e6943658dbf8f086987d8f2dc260b87e1439243e/binding-0.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-07-15 08:19:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "zauberzeug",
    "github_project": "binding",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "binding"
}
        
Elapsed time: 0.31787s