Skip to content

Commit 62d5810

Browse files
authored
Merge pull request #1359 from Textualize/validator-first-set
Call validator on first set
2 parents 21ab411 + 14a4ad4 commit 62d5810

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Fixed
1111

12+
- Fixed validator not running on first reactive set https://github.com/Textualize/textual/pull/1359
1213
- Ensure only printable characters are used as key_display https://github.com/Textualize/textual/pull/1361
1314

1415
## [0.6.0] - 2022-12-11

src/textual/reactive.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ def __set__(self, obj: Reactable, value: ReactiveType) -> None:
177177
validate_function = getattr(obj, f"validate_{name}", None)
178178
# Check if this is the first time setting the value
179179
first_set = getattr(obj, f"__first_set_{self.internal_name}", True)
180-
# Call validate, but not on first set.
181-
if callable(validate_function) and not first_set:
180+
# Call validate
181+
if callable(validate_function):
182182
value = validate_function(value)
183183
# If the value has changed, or this is the first time setting the value
184184
if current_value != value or first_set or self._always_update:

tests/test_reactive.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
import pytest
44

5-
from textual.app import App, ComposeResult
5+
from textual.app import App
66
from textual.reactive import reactive, var
7-
from textual.widget import Widget
87

98
OLD_VALUE = 5_000
109
NEW_VALUE = 1_000_000
@@ -81,14 +80,16 @@ async def watch_count(self, old_value: int, new_value: int) -> None:
8180
try:
8281
await asyncio.wait_for(app.watcher_called_event.wait(), timeout=0.05)
8382
except TimeoutError:
84-
pytest.fail("Async watcher wasn't called within timeout when reactive init = True")
83+
pytest.fail(
84+
"Async watcher wasn't called within timeout when reactive init = True")
8585

8686
assert app.count == OLD_VALUE
8787
assert app.watcher_old_value == OLD_VALUE
8888
assert app.watcher_new_value == OLD_VALUE # The value wasn't changed
8989

9090

91-
@pytest.mark.xfail(reason="Reactive watcher is incorrectly always called the first time it is set, even if value is same [issue#1230]")
91+
@pytest.mark.xfail(
92+
reason="Reactive watcher is incorrectly always called the first time it is set, even if value is same [issue#1230]")
9293
async def test_watch_init_false_always_update_false():
9394
class WatcherInitFalse(App):
9495
count = reactive(0, init=False)
@@ -173,20 +174,45 @@ def watch_value(self, new_value):
173174
assert app.watcher_called_with == OLD_VALUE
174175

175176

176-
@pytest.mark.xfail(reason="Validator methods not running when init=True [issue#1220]")
177177
async def test_validate_init_true():
178178
"""When init is True for a reactive attribute, Textual should call the validator
179179
AND the watch method when the app starts."""
180+
validator_call_count = 0
180181

181182
class ValidatorInitTrue(App):
182183
count = var(5, init=True)
183184

184185
def validate_count(self, value: int) -> int:
186+
nonlocal validator_call_count
187+
validator_call_count += 1
185188
return value + 1
186189

187190
app = ValidatorInitTrue()
188191
async with app.run_test():
192+
app.count = 5
189193
assert app.count == 6 # Validator should run, so value should be 5+1=6
194+
assert validator_call_count == 1
195+
196+
197+
async def test_validate_init_true_set_before_dom_ready():
198+
"""When init is True for a reactive attribute, Textual should call the validator
199+
AND the watch method when the app starts."""
200+
validator_call_count = 0
201+
202+
class ValidatorInitTrue(App):
203+
count = var(5, init=True)
204+
205+
def validate_count(self, value: int) -> int:
206+
nonlocal validator_call_count
207+
validator_call_count += 1
208+
return value + 1
209+
210+
app = ValidatorInitTrue()
211+
app.count = 5
212+
async with app.run_test():
213+
assert app.count == 6 # Validator should run, so value should be 5+1=6
214+
assert validator_call_count == 1
215+
190216

191217

192218
@pytest.mark.xfail(reason="Compute methods not called when init=True [issue#1227]")

0 commit comments

Comments
 (0)