Merge pull request #1359 from Textualize/validator-first-set

Call validator on first set
This commit is contained in:
Will McGugan
2022-12-16 20:11:36 +00:00
committed by GitHub
3 changed files with 34 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Fixed validator not running on first reactive set https://github.com/Textualize/textual/pull/1359
- Ensure only printable characters are used as key_display https://github.com/Textualize/textual/pull/1361
## [0.6.0] - 2022-12-11

View File

@@ -177,8 +177,8 @@ class Reactive(Generic[ReactiveType]):
validate_function = getattr(obj, f"validate_{name}", None)
# Check if this is the first time setting the value
first_set = getattr(obj, f"__first_set_{self.internal_name}", True)
# Call validate, but not on first set.
if callable(validate_function) and not first_set:
# Call validate
if callable(validate_function):
value = validate_function(value)
# If the value has changed, or this is the first time setting the value
if current_value != value or first_set or self._always_update:

View File

@@ -2,9 +2,8 @@ import asyncio
import pytest
from textual.app import App, ComposeResult
from textual.app import App
from textual.reactive import reactive, var
from textual.widget import Widget
OLD_VALUE = 5_000
NEW_VALUE = 1_000_000
@@ -81,14 +80,16 @@ async def test_watch_async_init_true():
try:
await asyncio.wait_for(app.watcher_called_event.wait(), timeout=0.05)
except TimeoutError:
pytest.fail("Async watcher wasn't called within timeout when reactive init = True")
pytest.fail(
"Async watcher wasn't called within timeout when reactive init = True")
assert app.count == OLD_VALUE
assert app.watcher_old_value == OLD_VALUE
assert app.watcher_new_value == OLD_VALUE # The value wasn't changed
@pytest.mark.xfail(reason="Reactive watcher is incorrectly always called the first time it is set, even if value is same [issue#1230]")
@pytest.mark.xfail(
reason="Reactive watcher is incorrectly always called the first time it is set, even if value is same [issue#1230]")
async def test_watch_init_false_always_update_false():
class WatcherInitFalse(App):
count = reactive(0, init=False)
@@ -173,20 +174,45 @@ async def test_reactive_with_callable_default():
assert app.watcher_called_with == OLD_VALUE
@pytest.mark.xfail(reason="Validator methods not running when init=True [issue#1220]")
async def test_validate_init_true():
"""When init is True for a reactive attribute, Textual should call the validator
AND the watch method when the app starts."""
validator_call_count = 0
class ValidatorInitTrue(App):
count = var(5, init=True)
def validate_count(self, value: int) -> int:
nonlocal validator_call_count
validator_call_count += 1
return value + 1
app = ValidatorInitTrue()
async with app.run_test():
app.count = 5
assert app.count == 6 # Validator should run, so value should be 5+1=6
assert validator_call_count == 1
async def test_validate_init_true_set_before_dom_ready():
"""When init is True for a reactive attribute, Textual should call the validator
AND the watch method when the app starts."""
validator_call_count = 0
class ValidatorInitTrue(App):
count = var(5, init=True)
def validate_count(self, value: int) -> int:
nonlocal validator_call_count
validator_call_count += 1
return value + 1
app = ValidatorInitTrue()
app.count = 5
async with app.run_test():
assert app.count == 6 # Validator should run, so value should be 5+1=6
assert validator_call_count == 1
@pytest.mark.xfail(reason="Compute methods not called when init=True [issue#1227]")