Browse Source

Move all async object creation to a proper initialisation point

This should make it so no object is created with another loop
pull/7680/head
Rapptz 3 years ago
parent
commit
9c61e10a55
  1. 54
      discord/client.py
  2. 6
      discord/http.py
  3. 5
      discord/shard.py
  4. 7
      discord/state.py

54
discord/client.py

@ -208,7 +208,7 @@ class Client:
self._connection: ConnectionState = self._get_state(**options) self._connection: ConnectionState = self._get_state(**options)
self._connection.shard_count = self.shard_count self._connection.shard_count = self.shard_count
self._closed: bool = False self._closed: bool = False
self._ready: asyncio.Event = asyncio.Event() self._ready: asyncio.Event = MISSING
self._connection._get_websocket = self._get_websocket self._connection._get_websocket = self._get_websocket
self._connection._get_client = lambda: self self._connection._get_client = lambda: self
@ -333,7 +333,7 @@ class Client:
def is_ready(self) -> bool: def is_ready(self) -> bool:
""":class:`bool`: Specifies if the client's internal cache is ready for use.""" """:class:`bool`: Specifies if the client's internal cache is ready for use."""
return self._ready.is_set() return self._ready is not MISSING and self._ready.is_set()
async def _run_event( async def _run_event(
self, self,
@ -445,6 +445,32 @@ class Client:
if not initial: if not initial:
await asyncio.sleep(5.0) await asyncio.sleep(5.0)
async def _async_setup_hook(self) -> None:
# Called whenever the client needs to initialise asyncio objects with a running loop
loop = asyncio.get_running_loop()
self.loop = loop
self.http.loop = loop
self._connection.loop = loop
await self._connection.async_setup()
self._ready = asyncio.Event()
async def setup_hook(self) -> None:
"""|coro|
A coroutine to be called to setup the bot, by default this is blank.
To perform asynchronous setup after the bot is logged in but before
it has connected to the Websocket, overwrite this coroutine.
This is only called once, in :meth:`login`, and will be called before
any events are dispatched, making it a better solution than doing such
setup in the :func:`~discord.on_ready` event.
.. versionadded:: 2.0
"""
pass
# login state management # login state management
async def login(self, token: str) -> None: async def login(self, token: str) -> None:
@ -472,10 +498,7 @@ class Client:
_log.info('logging in using static token') _log.info('logging in using static token')
loop = asyncio.get_running_loop() await self._async_setup_hook()
self.loop = loop
self.http.loop = loop
self._connection.loop = loop
data = await self.http.static_login(token.strip()) data = await self.http.static_login(token.strip())
self._connection.user = ClientUser(state=self._connection, data=data) self._connection.user = ClientUser(state=self._connection, data=data)
@ -617,22 +640,6 @@ class Client:
await self.login(token) await self.login(token)
await self.connect(reconnect=reconnect) await self.connect(reconnect=reconnect)
async def setup_hook(self) -> None:
"""|coro|
A coroutine to be called to setup the bot, by default this is blank.
To perform asynchronous setup after the bot is logged in but before
it has connected to the Websocket, overwrite this coroutine.
This is only called once, in :meth:`login`, and will be called before
any events are dispatched, making it a better solution than doing such
setup in the :func:`~discord.on_ready` event.
.. versionadded:: 2.0
"""
pass
def run(self, *args: Any, **kwargs: Any) -> None: def run(self, *args: Any, **kwargs: Any) -> None:
"""A blocking call that abstracts away the event loop """A blocking call that abstracts away the event loop
initialisation from you. initialisation from you.
@ -925,7 +932,8 @@ class Client:
Waits until the client's internal cache is all ready. Waits until the client's internal cache is all ready.
""" """
await self._ready.wait() if self._ready is not MISSING:
await self._ready.wait()
def wait_for( def wait_for(
self, self,

6
discord/http.py

@ -343,8 +343,7 @@ class HTTPClient:
self.connector: aiohttp.BaseConnector = connector or MISSING self.connector: aiohttp.BaseConnector = connector or MISSING
self.__session: aiohttp.ClientSession = MISSING # filled in static_login self.__session: aiohttp.ClientSession = MISSING # filled in static_login
self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary() self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
self._global_over: asyncio.Event = asyncio.Event() self._global_over: asyncio.Event = MISSING
self._global_over.set()
self.token: Optional[str] = None self.token: Optional[str] = None
self.bot_token: bool = False self.bot_token: bool = False
self.proxy: Optional[str] = proxy self.proxy: Optional[str] = proxy
@ -550,6 +549,9 @@ class HTTPClient:
self.__session = aiohttp.ClientSession( self.__session = aiohttp.ClientSession(
connector=self.connector, ws_response_class=DiscordClientWebSocketResponse, loop=self.loop connector=self.connector, ws_response_class=DiscordClientWebSocketResponse, loop=self.loop
) )
self._global_over = asyncio.Event()
self._global_over.set()
old_token = self.token old_token = self.token
self.token = token self.token = token

5
discord/shard.py

@ -424,8 +424,11 @@ class AutoShardedClient(Client):
self._connection.shards_launched.set() self._connection.shards_launched.set()
async def connect(self, *, reconnect: bool = True) -> None: async def _async_setup_hook(self) -> None:
await super()._async_setup_hook()
self.__queue = asyncio.PriorityQueue() self.__queue = asyncio.PriorityQueue()
async def connect(self, *, reconnect: bool = True) -> None:
self._reconnect = reconnect self._reconnect = reconnect
await self.launch_shards() await self.launch_shards()

7
discord/state.py

@ -300,6 +300,9 @@ class ConnectionState:
else: else:
await coro(*args, **kwargs) await coro(*args, **kwargs)
async def async_setup(self) -> None:
pass
@property @property
def self_id(self) -> Optional[int]: def self_id(self) -> Optional[int]:
u = self.user u = self.user
@ -1485,7 +1488,6 @@ class AutoShardedConnectionState(ConnectionState):
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.shard_ids: Union[List[int], range] = [] self.shard_ids: Union[List[int], range] = []
self.shards_launched: asyncio.Event = asyncio.Event()
def _update_message_references(self) -> None: def _update_message_references(self) -> None:
# self._messages won't be None when this is called # self._messages won't be None when this is called
@ -1500,6 +1502,9 @@ class AutoShardedConnectionState(ConnectionState):
# channel will either be a TextChannel, Thread or Object # channel will either be a TextChannel, Thread or Object
msg._rebind_cached_references(new_guild, channel) # type: ignore msg._rebind_cached_references(new_guild, channel) # type: ignore
async def async_setup(self) -> None:
self.shards_launched: asyncio.Event = asyncio.Event()
async def chunker( async def chunker(
self, self,
guild_id: int, guild_id: int,

Loading…
Cancel
Save