From 83b1fcc1026d4a7b141c19e8599c0f4f60198e64 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 1 May 2023 16:57:40 +0100 Subject: [PATCH] raise attribute error (#2443) * raise attribute error * fix compute defaults --- CHANGELOG.md | 9 +++++---- src/textual/reactive.py | 10 +++++++++- src/textual/widgets/_progress_bar.py | 1 - tests/test_reactive.py | 3 +++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc3903ab4..75bf2b7c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Changed + +- Setting attributes with a `compute_` method will now raise an `AttributeError` https://github.com/Textualize/textual/issues/2383 +- Unknown psuedo-selectors will now raise a tokenizer error (previously they were silently ignored) https://github.com/Textualize/textual/pull/2445 + ### Added - Watch methods can now optionally be private https://github.com/Textualize/textual/issues/2382 -### Changed - -- Unknown psuedo-selectors will now raise a tokenizer error (previously they were silently ignored) https://github.com/Textualize/textual/pull/2445 - ## [0.22.3] - 2023-04-29 ### Fixed diff --git a/src/textual/reactive.py b/src/textual/reactive.py index 5e2515de8..08259200d 100644 --- a/src/textual/reactive.py +++ b/src/textual/reactive.py @@ -174,6 +174,12 @@ class Reactive(Generic[ReactiveType]): _rich_traceback_omit = True self._initialize_reactive(obj, self.name) + + if hasattr(obj, self.compute_name): + raise AttributeError( + f"Can't set {obj}.{self.name!r}; reactive attributes with a compute method are read-only" + ) + name = self.name current_value = getattr(obj, name) # Check for validate function @@ -276,7 +282,9 @@ class Reactive(Generic[ReactiveType]): compute_method = getattr(obj, f"compute_{compute}") except AttributeError: continue - current_value = getattr(obj, f"_reactive_{compute}") + current_value = getattr( + obj, f"_reactive_{compute}", getattr(obj, f"_default_{compute}", None) + ) value = compute_method() setattr(obj, f"_reactive_{compute}", value) if value != current_value: diff --git a/src/textual/widgets/_progress_bar.py b/src/textual/widgets/_progress_bar.py index a51fc6fba..8eb6449fd 100644 --- a/src/textual/widgets/_progress_bar.py +++ b/src/textual/widgets/_progress_bar.py @@ -336,7 +336,6 @@ class ProgressBar(Widget, can_focus=False): self.show_percentage = show_percentage self.show_eta = show_eta - self.percentage = None self.total = total def compose(self) -> ComposeResult: diff --git a/tests/test_reactive.py b/tests/test_reactive.py index 2032406ae..1f6b47647 100644 --- a/tests/test_reactive.py +++ b/tests/test_reactive.py @@ -356,6 +356,9 @@ async def test_compute(): app.start = 10 assert app.count_double == 14 + with pytest.raises(AttributeError): + app.count_double = 100 + async def test_watch_compute(): """Check that watching a computed attribute works."""