From 9405059bd1892781e8237c3d302e3bf3d7ae4d51 Mon Sep 17 00:00:00 2001 From: denpamusic Date: Mon, 16 Oct 2023 01:31:31 +0300 Subject: [PATCH] Add custom filter. This filter allows to specify lambda to filter callback calls. --- docs/source/usage.rst | 17 +++++++++++++++++ pyplumio/filters.py | 31 ++++++++++++++++++++++++++++++- tests/test_filters.py | 18 +++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 7cec8948..06d51e73 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -498,6 +498,23 @@ It can be used with numeric values, dictionaries, tuples or lists. # and last await. ecomax.subscribe("outside_temp", delta(my_callback)) +Custom +^^^^^^ + +.. autofunction:: pyplumio.filters.custom + +This filter allows to specify filter function that will be called +every time the value is received from the controller. + +A callback is awaited once this filter function returns true. + +.. code-block:: python + + from pyplumio.filter import delta + + # Await the callback when temperature is higher that 10 degrees + # Celsius. + ecomax.subscribe("outside_temp", custom(my_callback, lambda x: x > 10)) Regulator Data -------------- diff --git a/pyplumio/filters.py b/pyplumio/filters.py index 6208dfd0..a7e13d2b 100644 --- a/pyplumio/filters.py +++ b/pyplumio/filters.py @@ -4,7 +4,7 @@ from abc import ABC, abstractmethod import math import time -from typing import Any, Final, SupportsFloat, overload +from typing import Any, Callable, Final, SupportsFloat, overload from pyplumio.const import UNDEFINED from pyplumio.helpers.parameter import Parameter @@ -254,3 +254,32 @@ def aggregate(callback: EventCallbackType, seconds: float) -> _Aggregate: over a specified time period. """ return _Aggregate(callback, seconds) + + +class _Custom(Filter): + """Represents a custom filter. + + Calls a callback with value, if user-defined filter function + that's called by this class with the value as an argument + returns true. + """ + + def __init__(self, callback: EventCallbackType, filter_fn: Callable[[Any], bool]): + """Initialize a new custom filter.""" + super().__init__(callback) + self._filter_fn = filter_fn + + async def __call__(self, new_value): + """Set a new value for the callback.""" + if self._filter_fn(new_value): + await self._callback(new_value) + + +def custom(callback: EventCallbackType, filter_fn: Callable[[Any], bool]) -> _Custom: + """A custom filter. + + A callback function will be called when user-defined filter + function, that's being called with the value as an argument, + returns true. + """ + return _Custom(callback, filter_fn) diff --git a/tests/test_filters.py b/tests/test_filters.py index 9b912a0c..536f26fe 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -5,7 +5,7 @@ import pytest -from pyplumio.filters import aggregate, debounce, delta, on_change, throttle +from pyplumio.filters import aggregate, custom, debounce, delta, on_change, throttle from pyplumio.helpers.parameter import Parameter from pyplumio.structures.alerts import Alert @@ -184,3 +184,19 @@ async def test_aggregate(mock_time) -> None: # Test with non-numeric value. with pytest.raises(ValueError): await wrapped_callback("banana") + + +async def test_custom() -> None: + """Test custom filter.""" + test_callback = AsyncMock() + wrapped_callback = custom(test_callback, lambda x: len(x) == 4) + + await wrapped_callback([1, 2]) + test_callback.assert_not_awaited() + + await wrapped_callback([1, 2, 3, 4]) + test_callback.assert_awaited_once_with([1, 2, 3, 4]) + test_callback.reset_mock() + + await wrapped_callback([]) + test_callback.assert_not_awaited()