Bases: Generic[T]
, IObservable[T]
A generic observable value that notifies listeners when its value changes.
Observable is the core building block of Observant's reactive system. It wraps
a single value and provides methods to get, set, and observe changes to that value.
Attributes:
Name |
Type |
Description |
_value |
T
|
The current value of the observable.
|
_callbacks |
list[Callable[[T], None]]
|
List of callback functions to be called when the value changes.
|
_on_change_enabled |
bool
|
Whether callbacks are enabled.
|
Examples:
# Create an observable integer
counter = Observable[int](0)
# Register a callback to be notified when the value changes
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Update the value
counter.set(1) # Prints: "Counter changed to 1"
# Get the current value
current_value = counter.get() # Returns: 1
Source code in observant\observable.py
| class Observable(Generic[T], IObservable[T]):
"""
A generic observable value that notifies listeners when its value changes.
Observable is the core building block of Observant's reactive system. It wraps
a single value and provides methods to get, set, and observe changes to that value.
Attributes:
_value: The current value of the observable.
_callbacks: List of callback functions to be called when the value changes.
_on_change_enabled: Whether callbacks are enabled.
Examples:
```python
# Create an observable integer
counter = Observable[int](0)
# Register a callback to be notified when the value changes
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Update the value
counter.set(1) # Prints: "Counter changed to 1"
# Get the current value
current_value = counter.get() # Returns: 1
```
"""
_value: T
_callbacks: list[Callable[[T], None]]
_on_change_enabled: bool = True
def __init__(self, value: T, *, on_change: Callable[[T], None] | None = None, on_change_enabled: bool = True) -> None:
"""
Initialize the Observable with a value.
Args:
value: The initial value of the observable.
on_change: Optional callback function to register immediately.
on_change_enabled: Whether callbacks should be enabled initially.
"""
print(f"DEBUG: Observable.__init__ called with value {value}")
self._value = value
self._callbacks = []
self._on_change_enabled = on_change_enabled
print("DEBUG: Observable.__init__ - Initialized with empty callbacks list")
@override
def get(self) -> T:
"""
Get the current value of the observable.
Returns:
The current value stored in this observable.
Examples:
```python
counter = Observable[int](0)
value = counter.get() # Returns: 0
```
"""
return self._value
@override
def set(self, value: T, notify: bool = True) -> None:
"""
Set a new value for the observable and notify all registered callbacks.
This method updates the internal value and, if notify is True and callbacks
are enabled, calls all registered callbacks with the new value.
Args:
value: The new value to set.
notify: Whether to notify the callbacks after setting the value.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Update with notification
counter.set(1) # Prints: "Counter changed to 1"
# Update without notification
counter.set(2, notify=False) # No output
```
"""
print(f"DEBUG: Observable.set called with value {value}")
self._value = value
if not notify or not self._on_change_enabled:
print("DEBUG: Observable.set - on_change is disabled, skipping callbacks")
return
print(f"DEBUG: Observable.set - Notifying {len(self._callbacks)} callbacks")
for i, callback in enumerate(self._callbacks):
print(f"DEBUG: Observable.set - Calling callback {i}")
callback(value)
print(f"DEBUG: Observable.set - Callback {i} completed")
print("DEBUG: Observable.set - Completed")
@override
def on_change(self, callback: Callable[[T], None]) -> None:
"""
Register a callback function to be called when the value changes.
The callback will be called with the new value whenever set() is called
with notify=True and callbacks are enabled. Callbacks are called in the
order they were registered.
If the same callback function is registered multiple times, it will only
be added once.
Args:
callback: A function that takes the new value as its argument.
Examples:
```python
counter = Observable[int](0)
# Register a callback
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Register another callback
counter.on_change(lambda value: print(f"Counter is now {value}"))
# Update the value
counter.set(1)
# Prints:
# "Counter changed to 1"
# "Counter is now 1"
```
"""
print(f"DEBUG: Observable.on_change called, current callbacks: {len(self._callbacks)}")
# Check if this callback is already registered to avoid duplicates
for existing_cb in self._callbacks:
if existing_cb == callback:
print("DEBUG: Observable.on_change - Callback already registered, skipping")
return
self._callbacks.append(callback)
print(f"DEBUG: Observable.on_change - Added callback, now have {len(self._callbacks)} callbacks")
@override
def enable(self) -> None:
"""
Enable the observable to notify changes.
After calling this method, subsequent calls to set() with notify=True
will trigger callbacks.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
# Enable notifications
counter.enable()
counter.set(2) # Prints: "Counter changed to 2"
```
"""
print("DEBUG: Observable.enable called")
self._on_change_enabled = True
@override
def disable(self) -> None:
"""
Disable the observable from notifying changes.
After calling this method, subsequent calls to set() will not trigger
callbacks, even if notify=True.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
```
"""
print("DEBUG: Observable.disable called")
self._on_change_enabled = False
def __bool__(self) -> bool:
"""
Convert the observable to a boolean.
This allows using the observable directly in boolean contexts.
Returns:
The boolean value of the current value.
Examples:
```python
counter = Observable[int](0)
if not counter:
print("Counter is zero") # This will print
counter.set(1)
if counter:
print("Counter is non-zero") # This will print
```
"""
return bool(self.get())
@override
def __str__(self) -> str:
"""
Convert the observable to a string.
This allows using the observable directly in string contexts.
Returns:
The string representation of the current value.
Examples:
```python
counter = Observable[int](42)
print(f"The counter is {counter}") # Prints: "The counter is 42"
```
"""
return str(self.get())
@override
def __repr__(self) -> str:
"""
Get the representation of the observable.
Returns:
A string representation of the observable, including its class name
and current value.
Examples:
```python
counter = Observable[int](42)
repr(counter) # Returns: "Observable(42)"
```
"""
return f"{self.__class__.__name__}({self.get()!r})"
|
__bool__()
Convert the observable to a boolean.
This allows using the observable directly in boolean contexts.
Returns:
Type |
Description |
bool
|
The boolean value of the current value.
|
Examples:
counter = Observable[int](0)
if not counter:
print("Counter is zero") # This will print
counter.set(1)
if counter:
print("Counter is non-zero") # This will print
Source code in observant\observable.py
| def __bool__(self) -> bool:
"""
Convert the observable to a boolean.
This allows using the observable directly in boolean contexts.
Returns:
The boolean value of the current value.
Examples:
```python
counter = Observable[int](0)
if not counter:
print("Counter is zero") # This will print
counter.set(1)
if counter:
print("Counter is non-zero") # This will print
```
"""
return bool(self.get())
|
__init__(value, *, on_change=None, on_change_enabled=True)
Initialize the Observable with a value.
Parameters:
Name |
Type |
Description |
Default |
value
|
T
|
The initial value of the observable.
|
required
|
on_change
|
Callable[[T], None] | None
|
Optional callback function to register immediately.
|
None
|
on_change_enabled
|
bool
|
Whether callbacks should be enabled initially.
|
True
|
Source code in observant\observable.py
| def __init__(self, value: T, *, on_change: Callable[[T], None] | None = None, on_change_enabled: bool = True) -> None:
"""
Initialize the Observable with a value.
Args:
value: The initial value of the observable.
on_change: Optional callback function to register immediately.
on_change_enabled: Whether callbacks should be enabled initially.
"""
print(f"DEBUG: Observable.__init__ called with value {value}")
self._value = value
self._callbacks = []
self._on_change_enabled = on_change_enabled
print("DEBUG: Observable.__init__ - Initialized with empty callbacks list")
|
__repr__()
Get the representation of the observable.
Returns:
Type |
Description |
str
|
A string representation of the observable, including its class name
|
str
|
|
Examples:
counter = Observable[int](42)
repr(counter) # Returns: "Observable(42)"
Source code in observant\observable.py
| @override
def __repr__(self) -> str:
"""
Get the representation of the observable.
Returns:
A string representation of the observable, including its class name
and current value.
Examples:
```python
counter = Observable[int](42)
repr(counter) # Returns: "Observable(42)"
```
"""
return f"{self.__class__.__name__}({self.get()!r})"
|
__str__()
Convert the observable to a string.
This allows using the observable directly in string contexts.
Returns:
Type |
Description |
str
|
The string representation of the current value.
|
Examples:
counter = Observable[int](42)
print(f"The counter is {counter}") # Prints: "The counter is 42"
Source code in observant\observable.py
| @override
def __str__(self) -> str:
"""
Convert the observable to a string.
This allows using the observable directly in string contexts.
Returns:
The string representation of the current value.
Examples:
```python
counter = Observable[int](42)
print(f"The counter is {counter}") # Prints: "The counter is 42"
```
"""
return str(self.get())
|
disable()
Disable the observable from notifying changes.
After calling this method, subsequent calls to set() will not trigger
callbacks, even if notify=True.
Examples:
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
Source code in observant\observable.py
| @override
def disable(self) -> None:
"""
Disable the observable from notifying changes.
After calling this method, subsequent calls to set() will not trigger
callbacks, even if notify=True.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
```
"""
print("DEBUG: Observable.disable called")
self._on_change_enabled = False
|
enable()
Enable the observable to notify changes.
After calling this method, subsequent calls to set() with notify=True
will trigger callbacks.
Examples:
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
# Enable notifications
counter.enable()
counter.set(2) # Prints: "Counter changed to 2"
Source code in observant\observable.py
| @override
def enable(self) -> None:
"""
Enable the observable to notify changes.
After calling this method, subsequent calls to set() with notify=True
will trigger callbacks.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Disable notifications
counter.disable()
counter.set(1) # No output
# Enable notifications
counter.enable()
counter.set(2) # Prints: "Counter changed to 2"
```
"""
print("DEBUG: Observable.enable called")
self._on_change_enabled = True
|
get()
Get the current value of the observable.
Returns:
Type |
Description |
T
|
The current value stored in this observable.
|
Examples:
counter = Observable[int](0)
value = counter.get() # Returns: 0
Source code in observant\observable.py
| @override
def get(self) -> T:
"""
Get the current value of the observable.
Returns:
The current value stored in this observable.
Examples:
```python
counter = Observable[int](0)
value = counter.get() # Returns: 0
```
"""
return self._value
|
on_change(callback)
Register a callback function to be called when the value changes.
The callback will be called with the new value whenever set() is called
with notify=True and callbacks are enabled. Callbacks are called in the
order they were registered.
If the same callback function is registered multiple times, it will only
be added once.
Parameters:
Name |
Type |
Description |
Default |
callback
|
Callable[[T], None]
|
A function that takes the new value as its argument.
|
required
|
Examples:
counter = Observable[int](0)
# Register a callback
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Register another callback
counter.on_change(lambda value: print(f"Counter is now {value}"))
# Update the value
counter.set(1)
# Prints:
# "Counter changed to 1"
# "Counter is now 1"
Source code in observant\observable.py
| @override
def on_change(self, callback: Callable[[T], None]) -> None:
"""
Register a callback function to be called when the value changes.
The callback will be called with the new value whenever set() is called
with notify=True and callbacks are enabled. Callbacks are called in the
order they were registered.
If the same callback function is registered multiple times, it will only
be added once.
Args:
callback: A function that takes the new value as its argument.
Examples:
```python
counter = Observable[int](0)
# Register a callback
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Register another callback
counter.on_change(lambda value: print(f"Counter is now {value}"))
# Update the value
counter.set(1)
# Prints:
# "Counter changed to 1"
# "Counter is now 1"
```
"""
print(f"DEBUG: Observable.on_change called, current callbacks: {len(self._callbacks)}")
# Check if this callback is already registered to avoid duplicates
for existing_cb in self._callbacks:
if existing_cb == callback:
print("DEBUG: Observable.on_change - Callback already registered, skipping")
return
self._callbacks.append(callback)
print(f"DEBUG: Observable.on_change - Added callback, now have {len(self._callbacks)} callbacks")
|
set(value, notify=True)
Set a new value for the observable and notify all registered callbacks.
This method updates the internal value and, if notify is True and callbacks
are enabled, calls all registered callbacks with the new value.
Parameters:
Name |
Type |
Description |
Default |
value
|
T
|
|
required
|
notify
|
bool
|
Whether to notify the callbacks after setting the value.
|
True
|
Examples:
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Update with notification
counter.set(1) # Prints: "Counter changed to 1"
# Update without notification
counter.set(2, notify=False) # No output
Source code in observant\observable.py
| @override
def set(self, value: T, notify: bool = True) -> None:
"""
Set a new value for the observable and notify all registered callbacks.
This method updates the internal value and, if notify is True and callbacks
are enabled, calls all registered callbacks with the new value.
Args:
value: The new value to set.
notify: Whether to notify the callbacks after setting the value.
Examples:
```python
counter = Observable[int](0)
counter.on_change(lambda value: print(f"Counter changed to {value}"))
# Update with notification
counter.set(1) # Prints: "Counter changed to 1"
# Update without notification
counter.set(2, notify=False) # No output
```
"""
print(f"DEBUG: Observable.set called with value {value}")
self._value = value
if not notify or not self._on_change_enabled:
print("DEBUG: Observable.set - on_change is disabled, skipping callbacks")
return
print(f"DEBUG: Observable.set - Notifying {len(self._callbacks)} callbacks")
for i, callback in enumerate(self._callbacks):
print(f"DEBUG: Observable.set - Calling callback {i}")
callback(value)
print(f"DEBUG: Observable.set - Callback {i} completed")
print("DEBUG: Observable.set - Completed")
|