Allow ui.View initialization without a running event loop

This commit is contained in:
Michael
2026-01-14 01:08:53 +02:00
committed by GitHub
parent b9b21ca270
commit bcea13e993

View File

@@ -219,7 +219,14 @@ class BaseView:
self.__cancel_callback: Optional[Callable[[BaseView], None]] = None self.__cancel_callback: Optional[Callable[[BaseView], None]] = None
self.__timeout_expiry: Optional[float] = None self.__timeout_expiry: Optional[float] = None
self.__timeout_task: Optional[asyncio.Task[None]] = None self.__timeout_task: Optional[asyncio.Task[None]] = None
self.__stopped: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
try:
loop = asyncio.get_running_loop()
except RuntimeError:
self.__stopped: Optional[asyncio.Future[bool]] = None
else:
self.__stopped: Optional[asyncio.Future[bool]] = loop.create_future()
self._total_children: int = len(tuple(self.walk_children())) self._total_children: int = len(tuple(self.walk_children()))
def _is_layout(self) -> bool: def _is_layout(self) -> bool:
@@ -557,7 +564,7 @@ class BaseView:
self.__timeout_task = asyncio.create_task(self.__timeout_task_impl()) self.__timeout_task = asyncio.create_task(self.__timeout_task_impl())
def _dispatch_timeout(self): def _dispatch_timeout(self):
if self.__stopped.done(): if self.__stopped is None or self.__stopped.done():
return return
if self.__cancel_callback: if self.__cancel_callback:
@@ -568,8 +575,8 @@ class BaseView:
asyncio.create_task(self.on_timeout(), name=f'discord-ui-view-timeout-{self.id}') asyncio.create_task(self.on_timeout(), name=f'discord-ui-view-timeout-{self.id}')
def _dispatch_item(self, item: Item, interaction: Interaction) -> Optional[asyncio.Task[None]]: def _dispatch_item(self, item: Item, interaction: Interaction) -> Optional[asyncio.Task[None]]:
if self.__stopped.done(): if self.__stopped is None or self.__stopped.done():
return return None
return asyncio.create_task(self._scheduled_task(item, interaction), name=f'discord-ui-view-dispatch-{self.id}') return asyncio.create_task(self._scheduled_task(item, interaction), name=f'discord-ui-view-dispatch-{self.id}')
@@ -600,7 +607,7 @@ class BaseView:
This operation cannot be undone. This operation cannot be undone.
""" """
if not self.__stopped.done(): if self.__stopped is not None and not self.__stopped.done():
self.__stopped.set_result(False) self.__stopped.set_result(False)
self.__timeout_expiry = None self.__timeout_expiry = None
@@ -614,6 +621,9 @@ class BaseView:
def is_finished(self) -> bool: def is_finished(self) -> bool:
""":class:`bool`: Whether the view has finished interacting.""" """:class:`bool`: Whether the view has finished interacting."""
if self.__stopped is None:
return True
return self.__stopped.done() return self.__stopped.done()
def is_dispatching(self) -> bool: def is_dispatching(self) -> bool:
@@ -642,6 +652,9 @@ class BaseView:
If ``True``, then the view timed out. If ``False`` then If ``True``, then the view timed out. If ``False`` then
the view finished normally. the view finished normally.
""" """
if self.__stopped is None:
self.__stopped = asyncio.get_running_loop().create_future()
return await self.__stopped return await self.__stopped
def walk_children(self) -> Generator[Item[Any], None, None]: def walk_children(self) -> Generator[Item[Any], None, None]: