Source code for colour.utilities.callback

"""
Callback Management
===================

Define the callback management objects.
"""

from __future__ import annotations

from collections import defaultdict
from dataclasses import dataclass

from colour.hints import (
    Any,
    Callable,
    List,
)

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = [
    "Callback",
    "MixinCallback",
]


[docs] @dataclass class Callback: """ Define a callback. Parameters ---------- name Callback name. function Callback callable. """ name: str function: Callable
[docs] class MixinCallback: """ A mixin providing support for callbacks. Attributes ---------- - :attr:`~colour.utilities.MixinCallback.callbacks` - :attr:`~colour.utilities.MixinCallback.__setattr__` Methods ------- - :meth:`~colour.utilities.MixinCallback.register_callback` - :meth:`~colour.utilities.MixinCallback.unregister_callback` Examples -------- >>> class WithCallback(MixinCallback): ... def __init__(self): ... super().__init__() ... self.attribute_a = "a" >>> with_callback = WithCallback() >>> def _on_attribute_a_changed(self, name: str, value: str) -> str: ... return value.upper() >>> with_callback.register_callback( ... "attribute_a", "on_attribute_a_changed", _on_attribute_a_changed ... ) >>> with_callback.attribute_a = "a" >>> with_callback.attribute_a 'A' """
[docs] def __init__(self) -> None: super().__init__() self._callbacks: defaultdict[str, List[Callback]] = defaultdict(list)
@property def callbacks(self) -> defaultdict[str, List[Callback]]: """ Getter property for the callbacks. Returns ------- :class:`defaultdict` Callbacks. """ return self._callbacks
[docs] def __setattr__(self, name: str, value: Any) -> None: """ Set given value to the attribute with given name. Parameters ---------- attribute Attribute to set the value of. value Value to set the attribute with. """ if hasattr(self, "_callbacks"): for callback in self._callbacks.get(name, []): value = callback.function(self, name, value) super().__setattr__(name, value)
[docs] def register_callback(self, attribute: str, name: str, function: Callable) -> None: """ Register the callback with given name for given attribute. Parameters ---------- attribute Attribute to register the callback for. name Callback name. function Callback callable. Examples -------- >>> class WithCallback(MixinCallback): ... def __init__(self): ... super().__init__() ... self.attribute_a = "a" ... >>> with_callback = WithCallback() >>> with_callback.register_callback( ... "attribute_a", "callback", lambda *args: None ... ) >>> with_callback.callbacks # doctest: +SKIP defaultdict(<class 'list'>, {'attribute_a': \ [Callback(name='callback', function=<function <lambda> at 0x...>)]}) """ self._callbacks[attribute].append(Callback(name, function))
[docs] def unregister_callback(self, attribute: str, name: str) -> None: """ Unregister the callback with given name for given attribute. Parameters ---------- attribute Attribute to unregister the callback for. name Callback name. Examples -------- >>> class WithCallback(MixinCallback): ... def __init__(self): ... super().__init__() ... self.attribute_a = "a" ... >>> with_callback = WithCallback() >>> with_callback.register_callback( ... "attribute_a", "callback", lambda s, n, v: v ... ) >>> with_callback.callbacks # doctest: +SKIP defaultdict(<class 'list'>, {'attribute_a': \ [Callback(name='callback', function=<function <lambda> at 0x...>)]}) >>> with_callback.unregister_callback("attribute_a", "callback") >>> with_callback.callbacks defaultdict(<class 'list'>, {}) """ if self._callbacks.get(attribute) is None: # pragma: no cover return self._callbacks[attribute] = [ callback for callback in self._callbacks.get(attribute, []) if callback.name != name ] if len(self._callbacks[attribute]) == 0: self._callbacks.pop(attribute, None)