textual
textual copied to clipboard
Decorator madness
Decorators for watch, compute, and validate. Works like this:
class MyWidget(Widget):
count = reactive(0)
double_count = reactive(0)
@count.watch
def count_changed(self, new_count:int):
...
@count.validate
def _max_ten(self, value:int) -> int:
return min(10, value)
@double_count.compute
def _double(self) -> int:
return self.count * 2
With this change, how would we override what a watcher does in a widget subclass?
If I have a count reactive declared in a widget which has a watcher defined, then I subclass that and I want my own watcher, how would I achieve this? In the child class, @count.watch doesn't work because count is not defined.
For example, this fails to run because NameError: name 'count' is not defined
class Parent(Widget):
count: Reactive[int] = reactive(0, init=False)
@count.watch
def _count_parent(self, new_value: int) -> None:
print("parent watcher")
class Child(Parent):
@count.watch
def _count_parent(self, new_value: int) -> None:
print("child watcher")
@Parent.count.watch doesn't work because of the "only a single method may be decorated with watch".
Even if that's solved, there's still a bit of a developer experience issue that @davep mentioned, whereby we no longer know what the parent watch method is called without going looking for it.
Going to park this for now. @darrenburns 's observation re inheritance may kill this idea entirely.
@willmcgugan Wondering if we should close this?
Think I'll keep it around for a bit. Would like to give it another pass at some point.
You can work around the NameError issue with the metaclass' __prepare__ method, e.g.
class M(type):
@classmethod
def __prepare__(cls, name, bases, **kwargs):
for base in bases:
if base.__name__ == 'A':
return dict(a=base.a)
return super().__prepare__(name, bases, **kwargs)
class A(metaclass=M):
a=1
class B(A):
print(a)
will print out 1.
When properly generalising this, I would create the MRO from the given list of bases, then go through them checking for attributes of type textual.reactive.reactive, and add the last one.