diff --git a/src/textual/app.py b/src/textual/app.py index 4cda806968..ccbe85d3cc 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3362,8 +3362,6 @@ async def _prune_nodes(self, widgets: list[Widget]) -> None: Args: widgets: Widgets to remove. """ - if not self.is_attached: - return async with self._dom_lock: for widget in widgets: await self._prune_node(widget) @@ -3375,6 +3373,7 @@ async def _prune_node(self, root: Widget) -> None: root: Node to remove. """ # Pruning a node that has been removed is a no-op + if root not in self._registry: return diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py index c6ef9decd4..b3fed6af72 100644 --- a/src/textual/message_pump.py +++ b/src/textual/message_pump.py @@ -247,8 +247,9 @@ def app(self) -> "App[object]": @property def is_attached(self) -> bool: """Is this node linked to the app through the DOM?""" + if self.is_dom_root: + return True node: MessagePump | None = self - while (node := node._parent) is not None: if node.is_dom_root: return True @@ -479,9 +480,10 @@ async def _close_messages(self, wait: bool = True) -> None: if self._closed or self._closing: return self._closing = True - stop_timers = list(self._timers) - for timer in stop_timers: - timer.stop() + await Timer._stop_all(self._timers) + # stop_timers = list(self._timers) + # for timer in stop_timers: + # timer.stop() self._timers.clear() await self._message_queue.put(events.Unmount()) Reactive._reset_object(self) diff --git a/src/textual/signal.py b/src/textual/signal.py index 8fe5d43a23..7128b5530b 100644 --- a/src/textual/signal.py +++ b/src/textual/signal.py @@ -109,7 +109,7 @@ def publish(self, data: SignalT) -> None: """ for node, callbacks in list(self._subscriptions.items()): - if not node.is_running: + if not (node.is_running and node.is_attached): # Removed nodes that are no longer running self._subscriptions.pop(node) else: