Added add_event_listener and remove_event_listener
This commit is contained in:
79
README.md
79
README.md
@@ -150,6 +150,82 @@ data.value = "final"
|
||||
# Callback2: test -> final
|
||||
```
|
||||
|
||||
#### Event listener
|
||||
I can register a listener for the `ObservableEvent.AFTER_PROPERTY_CHANGE` event.
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from myutils.observable import make_observable, bind, add_event_listener, ObservableEvent
|
||||
|
||||
@dataclass
|
||||
class Data:
|
||||
number: int = 1
|
||||
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
on_change_results = []
|
||||
on_after_change_results = []
|
||||
|
||||
def on_change(old, new):
|
||||
on_change_results.append((old, new))
|
||||
return new + 1
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
bind(data, 'number', on_change)
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "number", on_after_change)
|
||||
|
||||
data.number = 5
|
||||
data.number = 10
|
||||
|
||||
assert on_change_results == [(1, 5), (5, 10)]
|
||||
assert on_after_change_results == [("number", 1, 5, [6]), ("number", 5, 10, [11])]
|
||||
```
|
||||
|
||||
I can register for all attributes change events.
|
||||
|
||||
```python
|
||||
|
||||
from myutils.observable import make_observable, bind, add_event_listener, ObservableEvent
|
||||
|
||||
|
||||
class Data:
|
||||
number: int = 1
|
||||
value: str = "initial"
|
||||
|
||||
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
|
||||
def on_change_1(old, new):
|
||||
return new
|
||||
|
||||
|
||||
def on_change_2(old, new):
|
||||
return str(new) + "_1"
|
||||
|
||||
|
||||
on_after_change_results = []
|
||||
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "", on_after_change)
|
||||
bind(data, 'number', on_change_1)
|
||||
bind(data, 'number', on_change_2)
|
||||
bind(data, 'value', on_change_1)
|
||||
|
||||
data.number = 5
|
||||
data.value = "new value"
|
||||
|
||||
assert on_after_change_results == [("number", 1, 5, [5, "5_1"]), ("value", "initial", "new value", ["new value"])]
|
||||
```
|
||||
|
||||
#### Using Helper Functions
|
||||
|
||||
```python
|
||||
@@ -319,4 +395,5 @@ Special thanks to the Python and open-source community for their tools, inspirat
|
||||
|
||||
* 0.1.0 : Initial release
|
||||
* 0.2.0 : Observable results can be collected using `collect_return_values`
|
||||
* 0.3.0 : Added `unbind`, `unbind_all`, `has_listeners` `get_listener_count` to Observable
|
||||
* 0.3.0 : Added `unbind`, `unbind_all`, `has_listeners` `get_listener_count` to Observable
|
||||
* 0.4.0 : Added `add_event_listener` and `remove_event_listener`
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "myutils"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
description = "Base useful classes."
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class NotObservableError(Exception):
|
||||
def __init__(self, obj):
|
||||
super().__init__(f'{obj} is not observable')
|
||||
|
||||
|
||||
class ObservableEvent(Enum):
|
||||
AFTER_PROPERTY_CHANGE = "__*after_property_change*__"
|
||||
|
||||
|
||||
def make_observable(obj):
|
||||
if hasattr(obj, '_listeners'):
|
||||
return obj # Already observable
|
||||
@@ -24,6 +31,16 @@ def make_observable(obj):
|
||||
for callback in self._listeners[name]:
|
||||
res.append(callback(old_value, value))
|
||||
setattr(self, '_return_values', res)
|
||||
|
||||
# Trigger AFTER_PROPERTY_CHANGE event
|
||||
after_prop_changed_event = ObservableEvent.AFTER_PROPERTY_CHANGE.value
|
||||
if after_prop_changed_event in self._listeners:
|
||||
if "" in self._listeners[after_prop_changed_event]: # Trigger for all attributes
|
||||
for callback in self._listeners[after_prop_changed_event][""]:
|
||||
callback(name, old_value, value, res)
|
||||
else:
|
||||
for callback in self._listeners[after_prop_changed_event].get(name, []):
|
||||
callback(name, old_value, value, res)
|
||||
|
||||
obj.__class__ = ObservableVersion
|
||||
obj._listeners = {}
|
||||
@@ -48,12 +65,48 @@ def bind(obj, attr_name, callback):
|
||||
obj._listeners[attr_name].append(callback)
|
||||
|
||||
|
||||
"""
|
||||
Implementation of unbind() function for myutils.observable module.
|
||||
def add_event_listener(event: ObservableEvent, obj, attr_name, callback):
|
||||
if not hasattr(obj, '_listeners'):
|
||||
raise NotObservableError(
|
||||
f"Object must be made observable with make_observable() before binding"
|
||||
)
|
||||
event_name = event.value
|
||||
if event_name not in obj._listeners:
|
||||
obj._listeners[event_name] = {}
|
||||
if attr_name not in obj._listeners[event_name]:
|
||||
obj._listeners[event_name][attr_name] = []
|
||||
obj._listeners[event_name][attr_name].append(callback)
|
||||
|
||||
This implementation is adapted to work with the existing make_observable()
|
||||
and bind() functions that use the _listeners dictionary pattern.
|
||||
"""
|
||||
|
||||
def remove_event_listener(event: ObservableEvent, obj, attr_name, callback):
|
||||
if not hasattr(obj, '_listeners'):
|
||||
raise NotObservableError(
|
||||
f"Object must be made observable with make_observable() before binding"
|
||||
)
|
||||
event_name = event.value
|
||||
if event_name not in obj._listeners:
|
||||
return
|
||||
if attr_name and attr_name not in obj._listeners[event_name]:
|
||||
return
|
||||
|
||||
if not attr_name:
|
||||
for attr in list(obj._listeners[event_name].keys()):
|
||||
if callback:
|
||||
obj._listeners[event_name][attr].remove(callback)
|
||||
else:
|
||||
del obj._listeners[event_name][attr]
|
||||
else:
|
||||
if callback:
|
||||
obj._listeners[event_name][attr_name].remove(callback)
|
||||
else:
|
||||
del obj._listeners[event_name][attr_name]
|
||||
|
||||
# cleanup: remove empty attributes
|
||||
for attr in list(obj._listeners[event_name].keys()):
|
||||
if len(obj._listeners[event_name][attr]) == 0:
|
||||
del obj._listeners[event_name][attr]
|
||||
if len(obj._listeners[event_name]) == 0:
|
||||
del obj._listeners[event_name]
|
||||
|
||||
|
||||
def unbind(obj, attr_name, callback):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from myutils.observable import NotObservableError, make_observable, bind, collect_return_values, unbind, unbind_all, \
|
||||
has_listeners, get_listener_count
|
||||
has_listeners, get_listener_count, add_event_listener, ObservableEvent, remove_event_listener
|
||||
|
||||
|
||||
# Test fixtures
|
||||
@@ -552,6 +552,337 @@ def test_i_cannot_get_listener_count_on_non_observable_object(data):
|
||||
get_listener_count(data, "value")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# add_event_listener() tests
|
||||
# ============================================================================
|
||||
|
||||
def test_i_can_add_an_event_listener(observable_data):
|
||||
"""
|
||||
add_event_listener() should correctly add a listener for a specific event and attribute.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
add_event_listener(
|
||||
ObservableEvent.AFTER_PROPERTY_CHANGE, observable_data, "value", callback
|
||||
)
|
||||
|
||||
# Check that the listener has been added
|
||||
event_name = ObservableEvent.AFTER_PROPERTY_CHANGE.value
|
||||
assert event_name in observable_data._listeners
|
||||
assert "value" in observable_data._listeners[event_name]
|
||||
assert callback in observable_data._listeners[event_name]["value"]
|
||||
|
||||
|
||||
def test_i_can_add_multiple_event_listeners_for_same_attribute(observable_data):
|
||||
"""
|
||||
Multiple event listeners can be added to the same attribute and event.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback3(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "value", callback2)
|
||||
add_event_listener(event, observable_data, "value", callback3)
|
||||
|
||||
# Check that all listeners have been added
|
||||
event_name = event.value
|
||||
assert len(observable_data._listeners[event_name]["value"]) == 3
|
||||
assert callback1 in observable_data._listeners[event_name]["value"]
|
||||
assert callback2 in observable_data._listeners[event_name]["value"]
|
||||
assert callback3 in observable_data._listeners[event_name]["value"]
|
||||
|
||||
|
||||
def test_i_can_handle_duplicate_event_listeners(observable_data):
|
||||
"""
|
||||
The same callback can be added multiple times and should appear multiple times.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
# Check that the same callback appears 3 times
|
||||
event_name = event.value
|
||||
assert len(observable_data._listeners[event_name]["value"]) == 3
|
||||
assert observable_data._listeners[event_name]["value"].count(callback) == 3
|
||||
|
||||
|
||||
def test_i_cannot_add_listener_to_non_observable_object(data):
|
||||
"""
|
||||
Trying to add an event listener to a non-observable object should raise NotObservableError.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
with pytest.raises(NotObservableError, match="must be made observable"):
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "value", callback)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# remove_event_listener() tests
|
||||
# ============================================================================
|
||||
|
||||
def test_i_can_remove_an_event_listener(observable_data):
|
||||
"""
|
||||
remove_event_listener() should correctly remove a previously added listener.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
|
||||
# Add the listener
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
event_name = event.value
|
||||
assert callback in observable_data._listeners[event_name]["value"]
|
||||
|
||||
# Remove the listener
|
||||
remove_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
# Check that the listener has been removed and structure cleaned up
|
||||
assert event_name not in observable_data._listeners
|
||||
|
||||
|
||||
def test_i_can_remove_last_event_listener_and_clean_up_structure(observable_data):
|
||||
"""
|
||||
Removing the last listener should clean up the internal data structures.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
# Verify it was added
|
||||
assert event_name in observable_data._listeners
|
||||
assert "value" in observable_data._listeners[event_name]
|
||||
|
||||
# Remove it
|
||||
remove_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
# Verify cleanup: event should be removed from _listeners
|
||||
assert event_name not in observable_data._listeners
|
||||
|
||||
|
||||
def test_i_cannot_remove_listener_from_non_observable_object(data):
|
||||
"""
|
||||
Trying to remove an event listener from a non-observable object should raise NotObservableError.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
with pytest.raises(NotObservableError, match="must be made observable"):
|
||||
remove_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "value", callback)
|
||||
|
||||
|
||||
def test_i_cannot_remove_non_existent_event_listener(observable_data):
|
||||
"""
|
||||
Trying to remove a non-existent listener should fail silently (no exception).
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
|
||||
# Should not raise an exception
|
||||
remove_event_listener(event, observable_data, "value", callback)
|
||||
|
||||
|
||||
def test_remove_event_listener_does_not_affect_others(observable_data):
|
||||
"""
|
||||
Removing one listener should not affect other listeners on the same attribute.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback3(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Add three listeners
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "value", callback2)
|
||||
add_event_listener(event, observable_data, "value", callback3)
|
||||
|
||||
assert len(observable_data._listeners[event_name]["value"]) == 3
|
||||
|
||||
# Remove only the second one
|
||||
remove_event_listener(event, observable_data, "value", callback2)
|
||||
|
||||
# Check that only callback2 was removed
|
||||
assert len(observable_data._listeners[event_name]["value"]) == 2
|
||||
assert callback1 in observable_data._listeners[event_name]["value"]
|
||||
assert callback2 not in observable_data._listeners[event_name]["value"]
|
||||
assert callback3 in observable_data._listeners[event_name]["value"]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Integration tests for add/remove event listeners
|
||||
# ============================================================================
|
||||
|
||||
def test_i_can_add_and_remove_event_listeners_sequentially(observable_data):
|
||||
"""
|
||||
Multiple add/remove cycles should work correctly.
|
||||
"""
|
||||
|
||||
def callback(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Cycle 1: Add and remove
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
assert event_name in observable_data._listeners
|
||||
|
||||
remove_event_listener(event, observable_data, "value", callback)
|
||||
assert event_name not in observable_data._listeners
|
||||
|
||||
# Cycle 2: Add and remove again
|
||||
add_event_listener(event, observable_data, "value", callback)
|
||||
assert event_name in observable_data._listeners
|
||||
|
||||
remove_event_listener(event, observable_data, "value", callback)
|
||||
assert event_name not in observable_data._listeners
|
||||
|
||||
|
||||
def test_listener_removal_on_multiple_event_types(observable_data):
|
||||
"""
|
||||
Listeners for different attributes should be independently manageable.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Add listeners to different attributes
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "number", callback2)
|
||||
|
||||
assert "value" in observable_data._listeners[event_name]
|
||||
assert "number" in observable_data._listeners[event_name]
|
||||
|
||||
# Remove listener from "value"
|
||||
remove_event_listener(event, observable_data, "value", callback1)
|
||||
|
||||
# Check that "value" is removed but "number" remains
|
||||
assert "value" not in observable_data._listeners[event_name]
|
||||
assert "number" in observable_data._listeners[event_name]
|
||||
assert callback2 in observable_data._listeners[event_name]["number"]
|
||||
|
||||
|
||||
def test_i_can_remove_all_event_listener_when_attr_name_is_none(observable_data):
|
||||
"""
|
||||
When unbind_all() is called with attr_name=None, all listeners for all attributes
|
||||
should be removed from the observable object.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Add listeners to different attributes
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "value", callback2)
|
||||
add_event_listener(event, observable_data, "number", callback1)
|
||||
add_event_listener(event, observable_data, "number", callback2)
|
||||
|
||||
remove_event_listener(event, observable_data, None, callback1)
|
||||
callbacks = observable_data._listeners[event_name]["value"] + observable_data._listeners[event_name]["number"]
|
||||
assert callback1 not in callbacks
|
||||
|
||||
|
||||
def test_i_can_remove_all_event_listener_when_callback_is_none(observable_data):
|
||||
"""
|
||||
When unbind_all() is called with attr_name=None, all listeners for all attributes
|
||||
should be removed from the observable object.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Add listeners to different attributes
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "value", callback2)
|
||||
add_event_listener(event, observable_data, "number", callback1)
|
||||
add_event_listener(event, observable_data, "number", callback2)
|
||||
|
||||
remove_event_listener(event, observable_data, "value", None)
|
||||
assert "value" not in observable_data._listeners[event_name]
|
||||
|
||||
|
||||
def test_i_can_remove_all_event_listener_when_attr_name_callback_are_none(observable_data):
|
||||
"""
|
||||
When unbind_all() is called with attr_name=None, all listeners for all attributes
|
||||
should be removed from the observable object.
|
||||
"""
|
||||
|
||||
def callback1(attr, old, new, results):
|
||||
pass
|
||||
|
||||
def callback2(attr, old, new, results):
|
||||
pass
|
||||
|
||||
event = ObservableEvent.AFTER_PROPERTY_CHANGE
|
||||
event_name = event.value
|
||||
|
||||
# Add listeners to different attributes
|
||||
add_event_listener(event, observable_data, "value", callback1)
|
||||
add_event_listener(event, observable_data, "value", callback2)
|
||||
add_event_listener(event, observable_data, "number", callback1)
|
||||
add_event_listener(event, observable_data, "number", callback2)
|
||||
|
||||
remove_event_listener(event, observable_data, None, None)
|
||||
assert "value" not in observable_data._listeners[event_name]
|
||||
assert "number" not in observable_data._listeners[event_name]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Integration tests
|
||||
# ============================================================================
|
||||
@@ -584,24 +915,133 @@ def test_multiple_bind_unbind_cycles(observable_data):
|
||||
assert len(results) == 2
|
||||
|
||||
|
||||
def test_same_callback_can_be_bound_to_multiple_attributes(observable_data):
|
||||
"""
|
||||
The same callback can be bound to multiple attributes and unbound independently.
|
||||
"""
|
||||
results = []
|
||||
# ============================================================================
|
||||
# After property changed event
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def test_i_can_receive_changes_in_after_property_change_event(observable_data):
|
||||
"""Default case : after_property_change event should be fired when property changes."""
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
def callback(old, new):
|
||||
results.append((old, new))
|
||||
on_change_results = []
|
||||
on_after_change_results = []
|
||||
|
||||
bind(observable_data, "value", callback)
|
||||
bind(observable_data, "count", callback)
|
||||
def on_change(old, new):
|
||||
on_change_results.append((old, new))
|
||||
return new + 1
|
||||
|
||||
observable_data.value = "first"
|
||||
observable_data.count = 1
|
||||
assert len(results) == 2
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
unbind(observable_data, "value", callback)
|
||||
bind(data, 'number', on_change)
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "number", on_after_change)
|
||||
|
||||
observable_data.value = "second"
|
||||
observable_data.count = 2
|
||||
assert len(results) == 3 # Only count callback triggered
|
||||
data.number = 5
|
||||
data.number = 10
|
||||
|
||||
assert on_change_results == [(1, 5), (5, 10)]
|
||||
assert on_after_change_results == [("number", 1, 5, [6]), ("number", 5, 10, [11])]
|
||||
|
||||
|
||||
def test_i_can_receive_changes_when_multiple_properties_are_changed(observable_data):
|
||||
"""When multiple properties are changed, after_property_change event fires when all properties change."""
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
def on_change_1(old, new):
|
||||
return new
|
||||
|
||||
def on_change_2(old, new):
|
||||
return new + 1
|
||||
|
||||
on_after_change_results = []
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
bind(data, 'number', on_change_1)
|
||||
bind(data, 'number', on_change_2)
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "number", on_after_change)
|
||||
|
||||
data.number = 5
|
||||
data.number = 10
|
||||
|
||||
assert on_after_change_results == [("number", 1, 5, [5, 6]), ("number", 5, 10, [10, 11])]
|
||||
|
||||
|
||||
def test_i_can_receive_changes_in_after_property_change_event_when_declared_first(observable_data):
|
||||
"""after_property_change event should be fired even if defined before the bindings."""
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
def on_change(old, new):
|
||||
return new + 1
|
||||
|
||||
on_after_change_results = []
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "number", on_after_change)
|
||||
bind(data, 'number', on_change)
|
||||
|
||||
data.number = 5
|
||||
data.number = 10
|
||||
|
||||
assert on_after_change_results == [("number", 1, 5, [6]), ("number", 5, 10, [11])]
|
||||
|
||||
|
||||
def test_i_can_receive_changes_in_after_property_change_event_for_requested_attribute(observable_data):
|
||||
"""after_property_change event should be fired even if defined before the bindings."""
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
def on_change_1(old, new):
|
||||
return new
|
||||
|
||||
def on_change_2(old, new):
|
||||
return str(new) + "_1"
|
||||
|
||||
on_after_change_results = []
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "number", on_after_change)
|
||||
bind(data, 'number', on_change_1)
|
||||
bind(data, 'number', on_change_2)
|
||||
bind(data, 'value', on_change_1)
|
||||
|
||||
data.number = 5
|
||||
data.value = "new value"
|
||||
|
||||
assert on_after_change_results == [("number", 1, 5, [5, "5_1"])]
|
||||
|
||||
|
||||
def test_i_can_receive_changes_in_after_property_change_event_for_all_attributes(observable_data):
|
||||
"""after_property_change event should be fired even if defined before the bindings."""
|
||||
data = Data()
|
||||
make_observable(data)
|
||||
|
||||
def on_change_1(old, new):
|
||||
return new
|
||||
|
||||
def on_change_2(old, new):
|
||||
return str(new) + "_1"
|
||||
|
||||
on_after_change_results = []
|
||||
|
||||
def on_after_change(attr, old, new, results):
|
||||
on_after_change_results.append((attr, old, new, results))
|
||||
|
||||
add_event_listener(ObservableEvent.AFTER_PROPERTY_CHANGE, data, "", on_after_change)
|
||||
bind(data, 'number', on_change_1)
|
||||
bind(data, 'number', on_change_2)
|
||||
bind(data, 'value', on_change_1)
|
||||
|
||||
data.number = 5
|
||||
data.value = "new value"
|
||||
|
||||
assert on_after_change_results == [("number", 1, 5, [5, "5_1"]), ("value", "initial", "new value", ["new value"])]
|
||||
|
||||
Reference in New Issue
Block a user