From 289ac618a56b58807ff32db53aea7a3ef0c2b211 Mon Sep 17 00:00:00 2001 From: Paul Ollis Date: Sun, 20 Apr 2025 17:38:42 +0100 Subject: [PATCH] Create new event loop as fallback behaviour. The method App.push_screen tries to create a Future using `asyncio.get_running_loop()`. If this raises a RuntimeError it simply invokes `Future()`, as a fallback, order to support some of the tests. When running tests using pytest-xdist it is possible for this fallback mechanism to fail and instead raise another RuntimeError when `Future()` tries to get the 'standard' event loop. This is a rare occurrence with the current tests' structure, but I have seen it a number of times. It is causes by a test that invokes App.run (which invokes asyncio.run) being executed before a test that depends on the aforementioned fallback mechanism. Both tests must be executed by the same pytest-xdist worker process. --- src/textual/app.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 6861012863..e5c4e4f087 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -314,8 +314,8 @@ class App(Generic[ReturnType], DOMNode): scrollbar-background-active: ansi_default; scrollbar-color: ansi_blue; scrollbar-color-active: ansi_bright_blue; - scrollbar-color-hover: ansi_bright_blue; - scrollbar-corner-color: ansi_default; + scrollbar-color-hover: ansi_bright_blue; + scrollbar-corner-color: ansi_default; } .bindings-table--key { @@ -336,18 +336,18 @@ class App(Generic[ReturnType], DOMNode): } /* When a widget is maximized */ - Screen.-maximized-view { + Screen.-maximized-view { layout: vertical !important; hatch: right $panel; overflow-y: auto !important; align: center middle; .-maximized { - dock: initial !important; + dock: initial !important; } } /* Fade the header title when app is blurred */ - &:blur HeaderTitle { - text-opacity: 50%; + &:blur HeaderTitle { + text-opacity: 50%; } } *:disabled:can-focus { @@ -399,7 +399,7 @@ class MyApp(App[None]): ALLOW_SELECT: ClassVar[bool] = True """A switch to toggle arbitrary text selection for the app. - + Note that this doesn't apply to Input and TextArea which have builtin support for selection. """ @@ -445,7 +445,7 @@ class MyApp(App[None]): """The default value of [Screen.ALLOW_IN_MAXIMIZED_VIEW][textual.screen.Screen.ALLOW_IN_MAXIMIZED_VIEW].""" CLICK_CHAIN_TIME_THRESHOLD: ClassVar[float] = 0.5 - """The maximum number of seconds between clicks to upgrade a single click to a double click, + """The maximum number of seconds between clicks to upgrade a single click to a double click, a double click to a triple click, etc.""" BINDINGS: ClassVar[list[BindingType]] = [ @@ -472,7 +472,7 @@ class MyApp(App[None]): ESCAPE_TO_MINIMIZE: ClassVar[bool] = True """Use escape key to minimize widgets (potentially overriding bindings). - + This is the default value, used if the active screen's `ESCAPE_TO_MINIMIZE` is not changed from `None`. """ @@ -544,7 +544,7 @@ def __init__( self._registered_themes: dict[str, Theme] = {} """Themes that have been registered with the App using `App.register_theme`. - + This excludes the built-in themes.""" for theme in BUILTIN_THEMES.values(): @@ -746,7 +746,7 @@ def __init__( self.theme_changed_signal: Signal[Theme] = Signal(self, "theme-changed") """Signal that is published when the App's theme is changed. - + Subscribers will receive the new theme object as an argument to the callback. """ @@ -2681,10 +2681,13 @@ def push_screen( try: loop = asyncio.get_running_loop() except RuntimeError: - # Mainly for testing, when push_screen isn't called in an async context - future: asyncio.Future[ScreenResultType] = asyncio.Future() - else: - future = loop.create_future() + # Mainly for testing, when push_screen isn't called in an async context. + # Note: + # Creating a new event loop is necessary. Simply calling Future() + # without a loop parameter can fail when running tests using + # pytest-xdist. + loop = asyncio.new_event_loop() + future = loop.create_future() if self._screen_stack: self.screen.post_message(events.ScreenSuspend())