Skip to content

Commit

Permalink
Merge pull request #4255 from Textualize/await-complete
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan authored Mar 5, 2024
2 parents cb53ed3 + 19b3038 commit 1f66811
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 14 deletions.
17 changes: 7 additions & 10 deletions src/textual/await_complete.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
from __future__ import annotations

from asyncio import Future, gather
from typing import Any, Coroutine, Generator, TypeVar
from typing import Any, Awaitable, Generator

import rich.repr

ReturnType = TypeVar("ReturnType")


@rich.repr.auto(angular=True)
class AwaitComplete:
"""An 'optionally-awaitable' object."""
"""An 'optionally-awaitable' object which runs one or more coroutines (or other awaitables) concurrently."""

def __init__(self, *coroutines: Coroutine[Any, Any, Any]) -> None:
def __init__(self, *awaitables: Awaitable) -> None:
"""Create an AwaitComplete.
Args:
coroutines: One or more coroutines to execute.
awaitables: One or more awaitables to run concurrently.
"""
self.coroutines: tuple[Coroutine[Any, Any, Any], ...] = coroutines
self._future: Future[Any] = gather(*self.coroutines)
self._future: Future[Any] = gather(*awaitables)

async def __call__(self) -> Any:
return await self
Expand All @@ -29,12 +26,12 @@ def __await__(self) -> Generator[Any, None, Any]:

@property
def is_done(self) -> bool:
"""Returns True if the task has completed."""
"""`True` if the task has completed."""
return self._future.done()

@property
def exception(self) -> BaseException | None:
"""An exception if it occurred in any of the coroutines."""
"""An exception if the awaitables failed."""
if self._future.done():
return self._future.exception()
return None
Expand Down
3 changes: 2 additions & 1 deletion src/textual/widgets/_tabbed_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from asyncio import gather
from dataclasses import dataclass
from itertools import zip_longest
from typing import Awaitable

from rich.repr import Result
from rich.text import Text, TextType
Expand Down Expand Up @@ -449,7 +450,7 @@ def remove_pane(self, pane_id: str) -> AwaitComplete:
An optionally awaitable object that waits for the pane to be removed
and the Cleared message to be posted.
"""
removal_awaitables = [
removal_awaitables: list[Awaitable] = [
self.get_child_by_type(ContentTabs).remove_tab(
ContentTab.add_prefix(pane_id)
)
Expand Down
6 changes: 3 additions & 3 deletions src/textual/widgets/_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def clear(self) -> AwaitComplete:
underline.highlight_end = 0
self.call_after_refresh(self.post_message, self.Cleared(self))
self.active = ""
return AwaitComplete(self.query("#tabs-list > Tab").remove()())
return AwaitComplete(self.query("#tabs-list > Tab").remove())

def remove_tab(self, tab_or_id: Tab | str | None) -> AwaitComplete:
"""Remove a tab.
Expand All @@ -508,15 +508,15 @@ def remove_tab(self, tab_or_id: Tab | str | None) -> AwaitComplete:
An optionally awaitable object that waits for the tab to be removed.
"""
if not tab_or_id:
return AwaitComplete(self.app._remove_nodes([], None)())
return AwaitComplete(self.app._remove_nodes([], None))

if isinstance(tab_or_id, Tab):
remove_tab = tab_or_id
else:
try:
remove_tab = self.query_one(f"#tabs-list > #{tab_or_id}", Tab)
except NoMatches:
return AwaitComplete(self.app._remove_nodes([], None)())
return AwaitComplete(self.app._remove_nodes([], None))

removing_active_tab = remove_tab.has_class("-active")
next_tab = self._next_active
Expand Down

0 comments on commit 1f66811

Please sign in to comment.