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

App.on_unmount is called twice when running via pilot (App.run_test) #3246

Closed
mzebrak opened this issue Sep 6, 2023 · 4 comments
Closed

Comments

@mzebrak
Copy link

mzebrak commented Sep 6, 2023

Textual version: 0.36.0

There seems to be an issue with pilot causing on_unmount to be called twice.
Consider the following code:

import asyncio

from textual.app import App

UNMOUNT_TIMES = 0


async def test_on_unmount_is_called_once() -> None:
    class MyApp(App[None]):

        def on_unmount(self) -> None:
            global UNMOUNT_TIMES
            UNMOUNT_TIMES += 1

    async with MyApp().run_test():
        pass

    assert UNMOUNT_TIMES == 1, f"App.on_unmount should be called once, but it was called {UNMOUNT_TIMES} times"


if __name__ == "__main__":
    asyncio.run(test_on_unmount_is_called_once())

It results with AssertionError.

While this one is correct:

import asyncio

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.widgets import Footer

UNMOUNT_TIMES = 0


class MyApp(App[None]):
    BINDINGS = [
        Binding("q", "exit", "Exit"),
    ]

    def compose(self) -> ComposeResult:
        yield Footer()

    def on_unmount(self) -> None:
        global UNMOUNT_TIMES
        UNMOUNT_TIMES += 1

    def action_exit(self) -> None:
        self.exit()


if __name__ == "__main__":
    MyApp().run()
    assert UNMOUNT_TIMES == 1, f"App.on_unmount should be called once, but it was called {UNMOUNT_TIMES} times"
@github-actions
Copy link

github-actions bot commented Sep 6, 2023

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

@mzebrak
Copy link
Author

mzebrak commented Sep 6, 2023

Also I noticed there is a weird execution order.

Consider:

import asyncio
import logging

from textual.app import App

logging.basicConfig(handlers=[logging.StreamHandler()], level=logging.INFO)
logger = logging.getLogger("test")


async def test_on_unmount_ignoring_a_finally_block() -> None:
    class MyApp(App[None]):

        something = False

        def on_mount(self):
            self.something = True

        async def on_unmount(self) -> None:
            await self.close_something()

        async def close_something(self) -> None:
            if self.something:
                logger.info("closing something")
                try:
                    await self.very_slow_cleanup()
                finally:
                    logger.info("setting something to False")
                    self.something = False

        async def very_slow_cleanup(self):
            logger.info("very_slow_cleanup started")
            await asyncio.sleep(2)
            logger.info("very_slow_cleanup finished")

    async with MyApp().run_test():
        pass


if __name__ == "__main__":
    asyncio.run(test_on_unmount_ignoring_a_finally_block())

Now it causes the very_slow_cleanup to run twice because finally block is executed too late and it prints:

INFO:test:closing something
INFO:test:very_slow_cleanup started
INFO:test:closing something
INFO:test:very_slow_cleanup started
INFO:test:very_slow_cleanup finished
INFO:test:setting something to False
INFO:test:very_slow_cleanup finished
INFO:test:setting something to False

while it should be:

INFO:test:closing something
INFO:test:very_slow_cleanup started
INFO:test:very_slow_cleanup finished
INFO:test:setting something to False

@willmcgugan
Copy link
Collaborator

Fixed.

Copy link

github-actions bot commented Jul 9, 2024

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

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

2 participants