TUI freezes when calling pop_screen
right after a reactive attribute update has occurred
#3215
-
VERSION: When checking again with version Our application relies heavily on storing state in reactive stored class/dataclass attributes, where we keep certain data, because we have a lot of things to track (like 20 fields per account where you could have many accounts to observe), which we get from the external API. I just wanted to check if #3194 works for me, and it works, but in the different part of the application, I noticed that when calling pop_screen right after when reactive update happens - the whole app freezes. Unfortunately, I'm unable to provide minimal working example but here are some parts of the code which cause this to happen: We've got: Binding("f2", "add_to_cart", "Add to cart"), and def action_add_to_cart(self) -> None:
if self.__add_to_cart():
self.app.pop_screen() inside the self.app.world.update_reactive("profile_data") (which when is commented - causes pop screen to succeed, unless we accidentally trigger pop_screen in such a way that it will happen soon after the reactive update of the background worker)
class ManualReactive(DOMNode):
def update_reactive(self, attribute_name: str) -> None:
"""
Updates the given reactive attribute.
Reactive attributes of Textual are unable to detect changes to their own attributes
(if we are dealing with a non-primitive type like a class).
In order to notify watchers of a reactive attribute, it would have to be re-instantiated with the modified
attributes. (See https://github.com/Textualize/textual/discussions/1099#discussioncomment-4047932)
This is where this method comes in handy.
"""
try:
attribute = getattr(self, attribute_name)
except AttributeError as error:
raise AttributeError(f"{error}. Available ones are: {list(self._reactives)}") from error
descriptor = self.__class__.__dict__[attribute_name]
# now we trigger the descriptor.__set__ method like the `self.attribute_name = value` would do
if not descriptor._always_update:
# that means, watchers won't be notified unless __ne__ returns False, we could bypass with `always_update`
descriptor._always_update = True
setattr(self, attribute_name, attribute)
descriptor._always_update = False
else:
# we just need to trigger descriptor.__set__
setattr(self, attribute_name, attribute)
logger.debug(f"Manually updated reactive attribute {attribute_name} of {self.__class__.__name__}") In our logs we can observe, that the freeze happens when
What's intresting, the background worker keeps harvesting and processing the data though we got a TUI freezed (we use In the textual console (
|
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
Thank you for your issue. Give us a little time to review it. PS. You might want to check the FAQ if you haven't done so already. This is an automated reply, generated by FAQtory |
Beta Was this translation helpful? Give feedback.
-
You are doing unconventional things by directly accessing descriptors and private variables. I'd need to know if you can repeat the issue using reactives in the documented way. Regardless, the warnings indicate that callbacks have not completed after 3 seconds. It suggests a watcher or some other deferred callable is still running, or deadlocked waiting for something. Obviously I can't give any concrete suggestions without seeing code. |
Beta Was this translation helpful? Give feedback.
-
I removed the
Instead, used the documented way of But still, this issue persists. (0.35.1 was fine, observable on 0.36.0) |
Beta Was this translation helpful? Give feedback.
You are doing unconventional things by directly accessing descriptors and private variables. I'd need to know if you can repeat the issue using reactives in the documented way.
Regardless, the warnings indicate that callbacks have not completed after 3 seconds. It suggests a watcher or some other deferred callable is still running, or deadlocked waiting for something. Obviously I can't give any concrete suggestions without seeing code.