Replies: 5 comments 3 replies
-
Are all your tasks and threads created using the workers API? Tasks can be cancelled, and will receive a Exiting an app when there is concurrency involved can require some additional work. This isn't something exclusive to Textual, or even Python. Your example could be fixed by adding |
Beta Was this translation helpful? Give feedback.
-
Hi @willmcgugan Thanks for the quick answer. All my tasks and threads are created using the workers API. However, what I've noticed is that using the class DummyApp(App):
def compose(self) -> ComposeResult:
yield Static("This is a dummy Test GUI")
def on_mount(self):
self.call_after_refresh(self._some_very_expensive_task)
@work(thread=True)
def _some_very_expensive_task(self):
from time import sleep
sleep(10) When exiting the application via However, if I don't use import threading
class DummyApp(App):
def compose(self) -> ComposeResult:
yield Static("This is a dummy Test GUI")
def on_mount(self):
self.call_after_refresh(self._some_very_expensive_task)
def _some_very_expensive_task(self):
def _expensive_task():
from time import sleep
sleep(10)
thread = threading.Thread(target=_expensive_task)
thread.start()
if __name__ == "__main__":
app = DummyApp()
ret_val = app.run()
print(f"Application exited with {ret_val=!r}" In this second case, the call to |
Beta Was this translation helpful? Give feedback.
-
I understand. That makes sense. In that case, could you elaborate on
How exactly would that be setup for this small Also, you said
Just to make sure, this means while the worker is stuck on an |
Beta Was this translation helpful? Give feedback.
-
Got it! Makes sense. Other question I had was about when you said
Just to make sure, this means while the worker is stuck on an Additionally, you said that
Out of curiosity, what could be some "other options" ? |
Beta Was this translation helpful? Give feedback.
-
Thank you. I like the idea of having the job queue for a worker. From what I understand, each worker has a How exactly would that look like inside a Textual application ? Would I have to re do a lot of work from scratch ? Or is there a way to add this sort of mechanism onto the already existing My idea would be something like: import threading
from functools import partial
from queue import Queue, Empty as QueueEmpty
class MyWorker(threading.Thread):
def __init__(self, queue: Queue, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._queue = queue
self._active = True
def run(self) -> None:
while self._active:
try:
task = self._queue.get(timeout=1)
if task is None:
break # stop running
task()
self._queue.task_done()
except QueueEmpty:
pass # stop running
def force_cancel(self) -> None:
self._active = False
with self._queue.mutex:
self._queue.queue.clear() # remove all pending items
self._queue.put(None)
self.join()
def add_task(self, func, *args, **kwargs) -> None:
self._queue.put(partial(func, *args, **kwargs)) And then my textual application would use it like this: from textual.app import App
class DummyApp(App):
def action_quit(self) -> None:
self.worker.force_cancel() # force quit all tasks
self.app.exit()
def on_mount(self) -> None:
self.call_after_refresh(self._run_task)
def _run_task(self) -> None:
task_queue = Queue()
self.worker = MyWorker(task_queue)
self.worker.start()
for _ in range(5):
self.worker.add_task(self._my_task)
def _my_task(self):
from time import sleep
sleep(5)
if __name__ == "__main__":
app = DummyApp()
ret_val = app.run()
print(f"{ret_val=!r}") This stops at the end of the first |
Beta Was this translation helpful? Give feedback.
-
Possibility to force quit a Textual application
I developing a Textual application that I am attaching to a bigger main application. The Textual application is quite complex and makes many calls to workers in both Tasks and Threads.
The problem is that the way things currently stand, when I try to quit the Textual application to go back to the main one, the textual application will wait for all its worker threads to quit before completely quitting.
I would like a way to force quit the application and also force quit all its workers.
This Textual thread briefly speaks about the problem I am currently facing.
Example of problem
For an illustration of the problem, consider this application:
The problem is that when launching the application, if you quit the application via
^C
(ctrl_c
), the application will clear from the screen, however, the background task is still running, and you have to hit^C
again to quit the application completely.I am searching for a way to force quit the application, and all of its worker threads and tasks.
Attempted solution
I tried looking at the documentation but couldn't find anything, so I attempted to look at parts of Textual's code itself.
I tried overriding the
action_quit_app
function to cancel all the workers viaself.workers.cancel_all()
.Additionally, I wanted to force empty the message queue with a
self._close_messages(False)
. I'm pretty sure this function is not supposed to be called by the user, but I gave it a shot.Unfortunately, this still doesn't work and why I'm opening this discussion asking for help.
Beta Was this translation helpful? Give feedback.
All reactions