Added unbind, unbind_all, has_listeners get_listener_count
This commit is contained in:
173
README.md
173
README.md
@@ -29,14 +29,24 @@ occurs.
|
||||
- `bind(object, attribute, callback)`:
|
||||
Adds a callback function to an observable object's attribute. The callback is triggered when the attribute value
|
||||
changes.
|
||||
- `unbind(object, attribute, callback)`:
|
||||
Removes a callback from an observable object's attribute.
|
||||
- `unbind_all(object, attribute)`:
|
||||
Removes all callbacks from an observable object's attribute.
|
||||
- `has_listeners(object, attribute)`:
|
||||
Checks if an observable object has any listeners for a specific attribute.
|
||||
- `get_listener_count(object, attribute)`:
|
||||
Returns the number of listeners for a specific attribute on an observable object.
|
||||
- Exception: `NotObservableError` is raised if you attempt to bind a callback to a non-observable object.
|
||||
|
||||
#### Example Usage:
|
||||
|
||||
#### Basic Usage
|
||||
|
||||
Here is an example using the `Observable` module:
|
||||
|
||||
```python
|
||||
from myutils.observable import make_observable, bind
|
||||
from myutils.observable import make_observable, bind, unbind
|
||||
|
||||
|
||||
class Demo:
|
||||
@@ -47,11 +57,162 @@ class Demo:
|
||||
demo = Demo()
|
||||
make_observable(demo)
|
||||
|
||||
l = lambda old, new: print(f"Changed from {old} to {new}")
|
||||
|
||||
# Bind a callback to 'number'
|
||||
bind(demo, 'number', lambda old, new: print(f"Changed from {old} to {new}"))
|
||||
bind(demo, 'number', l)
|
||||
|
||||
# Updating an attribute triggers the callback
|
||||
demo.number = 42 # Output: Changed from 1 to 42
|
||||
|
||||
# Unbind the callback
|
||||
unbind(demo, 'number', l)
|
||||
demo.number = 43 # No output
|
||||
```
|
||||
|
||||
#### Multiple Callbacks
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from myutils.observable import make_observable, bind, unbind
|
||||
|
||||
|
||||
@dataclass
|
||||
class Data:
|
||||
value: str = "hello"
|
||||
|
||||
|
||||
data = Data()
|
||||
|
||||
|
||||
def callback1(old, new):
|
||||
print(f"Callback1: {old} -> {new}")
|
||||
|
||||
|
||||
def callback2(old, new):
|
||||
print(f"Callback2: {old} -> {new}")
|
||||
|
||||
|
||||
# Bind both
|
||||
bind(data, "value", callback1)
|
||||
bind(data, "value", callback2)
|
||||
|
||||
data.value = "test"
|
||||
# Prints:
|
||||
# Callback1: hello -> test
|
||||
# Callback2: hello -> test
|
||||
|
||||
# Remove only one
|
||||
unbind(data, "value", callback1)
|
||||
|
||||
data.value = "final"
|
||||
# Prints:
|
||||
# Callback2: test -> final
|
||||
```
|
||||
|
||||
#### Multiple Callbacks
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from myutils.observable import make_observable, bind, unbind
|
||||
|
||||
|
||||
@dataclass
|
||||
class Data:
|
||||
value: str = "hello"
|
||||
|
||||
|
||||
data = Data()
|
||||
|
||||
|
||||
def callback1(old, new):
|
||||
print(f"Callback1: {old} -> {new}")
|
||||
|
||||
|
||||
def callback2(old, new):
|
||||
print(f"Callback2: {old} -> {new}")
|
||||
|
||||
|
||||
# Bind both
|
||||
bind(data, "value", callback1)
|
||||
bind(data, "value", callback2)
|
||||
|
||||
data.value = "test"
|
||||
# Prints:
|
||||
# Callback1: hello -> test
|
||||
# Callback2: hello -> test
|
||||
|
||||
# Remove only one
|
||||
unbind(data, "value", callback1)
|
||||
|
||||
data.value = "final"
|
||||
# Prints:
|
||||
# Callback2: test -> final
|
||||
```
|
||||
|
||||
#### Using Helper Functions
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from myutils.observable import has_listeners, get_listener_count, unbind_all
|
||||
|
||||
|
||||
@dataclass
|
||||
class Data:
|
||||
value: str = "hello"
|
||||
|
||||
|
||||
data = Data()
|
||||
|
||||
# Check if attribute has listeners
|
||||
if has_listeners(data, "value"):
|
||||
print("Value has listeners")
|
||||
|
||||
# Get count
|
||||
count = get_listener_count(data, "value")
|
||||
print(f"Value has {count} listeners")
|
||||
|
||||
# Remove all listeners from one attribute
|
||||
unbind_all(data, "value")
|
||||
|
||||
# Remove all listeners from all attributes
|
||||
unbind_all(data)
|
||||
```
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
#### ❌ Wrong: Using different callback instance
|
||||
|
||||
```python
|
||||
bind(data, "value", lambda old, new: print(old))
|
||||
unbind(data, "value", lambda old, new: print(old)) # Different lambda!
|
||||
# The callback is NOT removed because lambdas are different objects
|
||||
```
|
||||
|
||||
#### ✅ Correct: Using same callback reference
|
||||
|
||||
```python
|
||||
callback = lambda old, new: print(old)
|
||||
bind(data, "value", callback)
|
||||
unbind(data, "value", callback) # Same reference
|
||||
# Callback is properly removed
|
||||
```
|
||||
|
||||
#### ❌ Wrong: Assuming unbind raises on missing callback
|
||||
|
||||
```python
|
||||
try:
|
||||
unbind(data, "value", non_existent_callback)
|
||||
except Exception:
|
||||
# This won't execute - unbind fails silently
|
||||
pass
|
||||
```
|
||||
|
||||
#### ✅ Correct: Check before unbinding if needed
|
||||
|
||||
```python
|
||||
if has_listeners(data, "value"):
|
||||
unbind(data, "value", callback)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -110,8 +271,9 @@ MyUtils
|
||||
│ └── __init__.py # Main package initialization
|
||||
├── tests
|
||||
│ ├── __init__.py # Test package initialization
|
||||
│ ├── test_observable.py # Tests for Observable module
|
||||
│ └── test_expando.py # Tests for Expando module
|
||||
│ ├── test_dummy.py # Tests for Dummy module
|
||||
│ ├── test_expando.py # Tests for Expando module
|
||||
│ └── test_observable.py # Tests for Observable module
|
||||
├── .gitignore # Git ignore file
|
||||
├── main.py # Application entry point
|
||||
├── requirements.txt # Project dependencies
|
||||
@@ -156,4 +318,5 @@ Special thanks to the Python and open-source community for their tools, inspirat
|
||||
## Release History
|
||||
|
||||
* 0.1.0 : Initial release
|
||||
* 0.2.0 : Observable results can be collected using `collect_return_values`
|
||||
* 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
|
||||
Reference in New Issue
Block a user