Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable waiting on workers to start #3473

Open
MorningLightMountain713 opened this issue Oct 6, 2023 · 1 comment
Open

Enable waiting on workers to start #3473

MorningLightMountain713 opened this issue Oct 6, 2023 · 1 comment

Comments

@MorningLightMountain713
Copy link

MorningLightMountain713 commented Oct 6, 2023

Feature request, or, let me know if there is an easier way to do this.

Use case: To be able to visually notify users when work is being done. I.e, when workers are being run by Textual.

Currently, there is no way to await the WorkerManager for when work is added. I would like to use this "leading edge" to trigger dom events. (I'm using a spinner) - the ability to await the "trailing edge" is available with WorkerManager.wait_for_complete

I have modified the WorkerManager and Worker classes, and the work decorator.

Here is how I'm using it in a Textual app:

    @contextmanager
    def spinner(self) -> Generator[None, None, None]:
        try:
            self.mount(Spinner("line"), before=0)
            yield
        finally:
            self.query_one("Spinner", Spinner).remove()

    @work(transient=False)
    async def manage_spinner(self) -> None:
        """Adds / Removes a spinner whenever transient Textual workers are run.
        """
        while True:
            await self.workers.wait_for_added()
            with self.spinner():
                await self.workers.wait_for_complete()

For this to make sense, I added the concept of a transient worker. So workers can either be transient, or long running.

Here is the new method (and modified wait_for_complete) on the WorkerManager

    async def wait_for_complete(self, workers: Iterable[Worker] | None = None) -> None:
        """Wait for workers to complete.

        Args:
            workers: An iterable of workers or None to wait for all workers in the manager.
        """

        await asyncio.gather(*[worker.wait() for worker in (workers or self) if worker.transient])

    async def wait_for_added(self) -> None:
        """Wait for a transient worker to be added. If a worker is already added, return immediately."""
        await self._worker_added.wait()

Then when the first transient worker gets added an event is set, and cleared when the last transient worker is removed.

This is what it looks like in the app demo below. The top screen is the app, where there is a spinner in the top left corner whenever there is textual work being done. The bottom screen is nettop, where you can see the actual network io from the textual workers

textual_app.mov

If this makes sense, happy to do a PR.

@github-actions
Copy link

github-actions bot commented Oct 6, 2023

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant