Skip to main content

Your one-stop-shop for observer synchronization.

Project description

Munin

The observer pattern implementation with a cool name.

Features:

  • Simple observer implementation with Observer (Protocol) and ObservableMixin.
  • Concept of "discretion" to break infinite call-back loops.
  • Simple, No python wizardry!
  • Easily control behaviour with decorators @notify and @discrete.

Goals:

  • Simplicity: Limit python wizardry
  • Concise: SLOC should never reach thousands
  • Flexible: Should be able to cover every Pythonista's observing needs.
  • Typed: Keep munin typed and type-safe to the furthest extent possible.
  • Well tested: Code coverage should be "at least" 100%.

Walk-through

Say we have a text field from some GUI-framework, FrameworkTextField. It's common for GUI-frameworks to offer some synchronization mechanism of their own to handle certain events (user input in a text field, for example), be it signals, call-backs or black magic.

class FrameworkTextField:
    def __init__(self):
        self.content: str = "<Placeholder text>"
        self.content_changed_callbacks: List[Callable[str, ...]] = []

    def set_content(self, new_content):
        self.content = new_content
        self.on_content_changed()

    def on_content_changed(self):
        for callback in self.content_changed_callbacks:
            callback(self.content)

One is easily tempted to use the framework's synchronization mechanisms to keep their model synced, but as in Uncle Bob's words; "We should be sceptic of frameworks" and most important of all: We should not depend on them.

To separate view and model, you can do something like this with munin:

from munin import Observer, ObservableMixin

class MyTextModel(ObservableMixin):
    """
    Basically an observable str.
    """
    def __init__(self):
        super().__init__()
        self.text: str = ""

    def set_text(self, new_text: str):
        self.text = new_text
        self.notify()  # alternatively decorator `@notify`

    # Implemented in ObservableMixin:
    #
    # * def add_observer(self, observer): ...
    # * def notify(self): ...


class MyTextField(FrameworkTextField, Observer[MyTextModel]):
    def __init__(self, model):
        FrameworkTextField.__init__(self)

        model.add_observer(self)
        self.content_changed_callbacks.append(model.set_text)

    def act(self, observable: MyTextModel):
        """
        act() is munin's "update"-function.
        When an Observable notify:s, the Observable passes itself through this function to
        all its Observers.
        """
        self.set_content(observable.content)

model = MyTextModel()
MyTextField(model)

The keen reader sees that a call to MyTextField.set_content(...) will start an infinite loop. This can be combatted with "Discretion", litteraly. "Discretion" is the munin-way to temporarily turn off the observer synchronization.

from munin import discretion, discrete, ...

class MyTextField(FrameworkTextField, Observer[MyTextModel]):
    ...

    @discrete  # "Discretion" with a decorator
    def act(self, observable: MyTextModel):
        # "Discretion" with a context manager
        with discretion:
            self.set_content(observable.content)

Some GUI frameworks (PySide for example) experience metaclass conflicts when doing multiple inheritance like in this example. Luckily, Observer is a Protocol, which means that the inheritance can be omitted without any repercussions.

class MyTextField(FrameworkTextField):
    def act(self, observable: MyTextModel):
        """Still satisfies the Observer Protocol"""
        pass

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

munin-observers-0.2.1.tar.gz (5.0 kB view details)

Uploaded Source

Built Distribution

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

munin_observers-0.2.1-py3-none-any.whl (5.3 kB view details)

Uploaded Python 3

File details

Details for the file munin-observers-0.2.1.tar.gz.

File metadata

  • Download URL: munin-observers-0.2.1.tar.gz
  • Upload date:
  • Size: 5.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.0 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.8.10

File hashes

Hashes for munin-observers-0.2.1.tar.gz
Algorithm Hash digest
SHA256 6c80ccacea5cb7d2e26779fc9ee41c182376e69ba3e45f9436f66a3bd208d187
MD5 8aa03e1b1bf26bbb0fc16a3c639de14f
BLAKE2b-256 61b6ecdf903faf857f182d9a1dd58c896a7ad52934c17d486729e1f0b99f0e79

See more details on using hashes here.

File details

Details for the file munin_observers-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: munin_observers-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 5.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.0 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.8.10

File hashes

Hashes for munin_observers-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7628c66e9302e8738932a955c4083c9b7f5678b271486f045bed9d3b84939be9
MD5 07daf24c50b9a12c316d985235bcee79
BLAKE2b-256 00ed8c234b3d81898ada08757b42c75110ea58acb38ef0d0256ced18d57b3cf5

See more details on using hashes here.

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