From dce0206a4352e6f24a56d76e66cfa01e752a622c Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 3 May 2025 00:37:49 +0300 Subject: [PATCH 001/111] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e40955afe..d59b05caa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,9 @@ These changes are available on the `master` branch, but have not yet been releas ([#2746])(https://github.com/Pycord-Development/pycord/pull/2746) - Updated `valid_locales` to support `in` and `es-419`. ([#2767])(https://github.com/Pycord-Development/pycord/pull/2767) +- Fixed support emoji aliases like `:smile:` in PartialEmoji.from_str + ([#2774](https://github.com/Pycord-Development/pycord/pull/2774)) + ### Changed From b2b888471bc67524f4345e21f4635b3353191fca Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 3 May 2025 23:13:42 +0300 Subject: [PATCH 002/111] add a get or fetch to guild Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/discord/guild.py b/discord/guild.py index 337abd31c0..0f8ae7afd5 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -38,6 +38,8 @@ Tuple, Union, overload, + TypeVar, + Type ) from . import abc, utils @@ -863,6 +865,66 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) + _FETCHABLE = TypeVar( + "_FETCHABLE", + bound=Union[ + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + Member, + ], + ) + + async def get_or_fetch( + self: Guild, + object_type: Type[_FETCHABLE], + object_id: int, + ) -> Optional[_FETCHABLE]: + """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. + + Parameters + ---------- + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`] + Type of object to fetch or get. + + object_id: :class:`int` + ID of object to get. + + Returns + ------- + + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] + The object of type that was specified or ``None`` if not found. + + """ + if object_type is Member: + return await utils.get_or_fetch( + obj=self, + attr="member", + id=object_id, + default=None + ) + + elif object_type in ( + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + ): + return await utils.get_or_fetch( + obj=self, + attr="channel", + id=object_id, + default=None + ) + + raise InvalidArgument(f"Class {object_type.__name__} cannot be used with discord.Guild.get_or_fetch()") + @property def premium_subscribers(self) -> list[Member]: """A list of members who have "boosted" this guild.""" From 8f9a257b9336bbb4b83372bc3cc65dfee6499cd0 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 3 May 2025 23:50:40 +0300 Subject: [PATCH 003/111] Implement better get_or_fetch using typevar and object Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 119 +++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 363d339391..c02fba3a88 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -61,7 +61,23 @@ TypeVar, Union, overload, + Type, + Optional ) +if TYPE_CHECKING: + from discord import ( + Client, + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + Member, + User, + Guild, + ) + from .errors import HTTPException, InvalidArgument @@ -573,65 +589,78 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: return None -async def get_or_fetch(obj, attr: str, id: int, *, default: Any = MISSING) -> Any: - """|coro| - - Attempts to get an attribute from the object in cache. If it fails, it will attempt to fetch it. - If the fetch also fails, an error will be raised. +_FETCHABLE = TypeVar( + "_FETCHABLE", + bound=Union[ + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + Member, + User, + Guild, + ], + ) +async def get_or_fetch( + obj: Guild | Client, + object_type: Type[_FETCHABLE], + object_id: int, + default: Any = MISSING +) -> Optional[_FETCHABLE]: + """ + Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. Parameters ---------- - obj: Any - The object to use the get or fetch methods in - attr: :class:`str` - The attribute to get or fetch. Note the object must have both a ``get_`` and ``fetch_`` method for this attribute. - id: :class:`int` - The ID of the object - default: Any - The default value to return if the object is not found, instead of raising an error. + obj : Guild | Client + The object to operate on. + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`] + Type of object to fetch or get. + + object_id: :class:`int` + ID of object to get. + + default : Any, optional + A default to return instead of raising if fetch fails. Returns ------- - Any - The object found or the default value. - - Raises - ------ - :exc:`AttributeError` - The object is missing a ``get_`` or ``fetch_`` method - :exc:`NotFound` - Invalid ID for the object - :exc:`HTTPException` - An error occurred fetching the object - :exc:`Forbidden` - You do not have permission to fetch the object - Examples - -------- - - Getting a guild from a guild ID: :: + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] + The object of type that was specified or ``None`` if not found. - guild = await utils.get_or_fetch(client, 'guild', guild_id) + """ + if object_type.__name__ in {"Member", "User", "Guild"}: + attr = object_type.__name__.lower() + elif object_type.__name__ in { + "VoiceChannel", + "TextChannel", + "ForumChannel", + "StageChannel", + "CategoryChannel", + "Thread", + }: + attr = "channel" + else: + raise InvalidArgument(f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()") - Getting a channel from the guild. If the channel is not found, return None: :: + + getter = getattr(obj, f"get_{attr}", None) + if getter: + result = getter(object_id) + if result is not None: + return result - channel = await utils.get_or_fetch(guild, 'channel', channel_id, default=None) - """ - getter = getattr(obj, f"get_{attr}")(id) - if getter is None: + fetcher = getattr(obj, f"fetch_{attr}", None) or getattr(obj, f"_fetch_{attr}", None) + if fetcher: try: - getter = await getattr(obj, f"fetch_{attr}")(id) - except AttributeError: - getter = await getattr(obj, f"_fetch_{attr}")(id) - if getter is None: - raise ValueError(f"Could not find {attr} with id {id} on {obj}") + return await fetcher(object_id) except (HTTPException, ValueError): if default is not MISSING: return default - else: - raise - return getter - + raise def _unique(iterable: Iterable[T]) -> list[T]: return [x for x in dict.fromkeys(iterable)] From 9f6219c11cdd2f33f8c7132fe5369efba99d1d9e Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 3 May 2025 23:52:57 +0300 Subject: [PATCH 004/111] shortcut for get or fetch Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 0f8ae7afd5..50734a2805 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -882,6 +882,7 @@ async def get_or_fetch( self: Guild, object_type: Type[_FETCHABLE], object_id: int, + default: Any = MISSING ) -> Optional[_FETCHABLE]: """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. @@ -892,7 +893,10 @@ async def get_or_fetch( object_id: :class:`int` ID of object to get. - + + default : Any, optional + A default to return instead of raising if fetch fails. + Returns ------- @@ -900,30 +904,14 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ - if object_type is Member: - return await utils.get_or_fetch( - obj=self, - attr="member", - id=object_id, - default=None - ) - - elif object_type in ( - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - ): - return await utils.get_or_fetch( - obj=self, - attr="channel", - id=object_id, - default=None - ) + return await utils.get_or_fetch( + obj=self, + object_type=object_type, + object_id=object_id, + default=default + ) - raise InvalidArgument(f"Class {object_type.__name__} cannot be used with discord.Guild.get_or_fetch()") + @property def premium_subscribers(self) -> list[Member]: From 4af514956bb0a88835c2831c5b87c9c6d6c3a205 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 3 May 2025 23:56:50 +0300 Subject: [PATCH 005/111] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d59b05caa7..40189c314f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,9 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) - +- Added `Guild.get_or_fetch()` shortcut method and better get_or_fetch + ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) + ### Fixed - Fixed `Enum` options not setting the correct type when only one choice is available. @@ -105,8 +107,6 @@ These changes are available on the `master` branch, but have not yet been releas ([#2746])(https://github.com/Pycord-Development/pycord/pull/2746) - Updated `valid_locales` to support `in` and `es-419`. ([#2767])(https://github.com/Pycord-Development/pycord/pull/2767) -- Fixed support emoji aliases like `:smile:` in PartialEmoji.from_str - ([#2774](https://github.com/Pycord-Development/pycord/pull/2774)) ### Changed From 6f59fcd6dd16cfb839c20636fa523cd06b13cb04 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 20:58:35 +0000 Subject: [PATCH 006/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 3 +-- discord/guild.py | 22 ++++++++------------ discord/utils.py | 53 ++++++++++++++++++++++++++---------------------- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40189c314f..b36965cf13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) - Added `Guild.get_or_fetch()` shortcut method and better get_or_fetch ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) - + ### Fixed - Fixed `Enum` options not setting the correct type when only one choice is available. @@ -108,7 +108,6 @@ These changes are available on the `master` branch, but have not yet been releas - Updated `valid_locales` to support `in` and `es-419`. ([#2767])(https://github.com/Pycord-Development/pycord/pull/2767) - ### Changed - Renamed `cover` property of `ScheduledEvent` and `cover` argument of diff --git a/discord/guild.py b/discord/guild.py index 50734a2805..c5d898328d 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -36,10 +36,10 @@ Optional, Sequence, Tuple, + Type, + TypeVar, Union, overload, - TypeVar, - Type ) from . import abc, utils @@ -880,10 +880,10 @@ def get_member(self, user_id: int, /) -> Member | None: async def get_or_fetch( self: Guild, - object_type: Type[_FETCHABLE], + object_type: type[_FETCHABLE], object_id: int, - default: Any = MISSING - ) -> Optional[_FETCHABLE]: + default: Any = MISSING, + ) -> _FETCHABLE | None: """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. Parameters @@ -893,26 +893,20 @@ async def get_or_fetch( object_id: :class:`int` ID of object to get. - + default : Any, optional A default to return instead of raising if fetch fails. - + Returns ------- Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] The object of type that was specified or ``None`` if not found. - """ return await utils.get_or_fetch( - obj=self, - object_type=object_type, - object_id=object_id, - default=default + obj=self, object_type=object_type, object_id=object_id, default=default ) - - @property def premium_subscribers(self) -> list[Member]: """A list of members who have "boosted" this guild.""" diff --git a/discord/utils.py b/discord/utils.py index c02fba3a88..0d0b2c764d 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -56,14 +56,15 @@ Iterator, Literal, Mapping, + Optional, Protocol, Sequence, + Type, TypeVar, Union, overload, - Type, - Optional ) + if TYPE_CHECKING: from discord import ( Client, @@ -78,7 +79,6 @@ Guild, ) - from .errors import HTTPException, InvalidArgument try: @@ -590,25 +590,27 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( - "_FETCHABLE", - bound=Union[ - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - Member, - User, - Guild, - ], - ) + "_FETCHABLE", + bound=Union[ + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + Member, + User, + Guild, + ], +) + + async def get_or_fetch( obj: Guild | Client, - object_type: Type[_FETCHABLE], + object_type: type[_FETCHABLE], object_id: int, - default: Any = MISSING -) -> Optional[_FETCHABLE]: + default: Any = MISSING, +) -> _FETCHABLE | None: """ Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. @@ -621,7 +623,7 @@ async def get_or_fetch( object_id: :class:`int` ID of object to get. - + default : Any, optional A default to return instead of raising if fetch fails. @@ -630,7 +632,6 @@ async def get_or_fetch( Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] The object of type that was specified or ``None`` if not found. - """ if object_type.__name__ in {"Member", "User", "Guild"}: attr = object_type.__name__.lower() @@ -644,16 +645,19 @@ async def get_or_fetch( }: attr = "channel" else: - raise InvalidArgument(f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()") + raise InvalidArgument( + f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + ) - getter = getattr(obj, f"get_{attr}", None) if getter: result = getter(object_id) if result is not None: return result - fetcher = getattr(obj, f"fetch_{attr}", None) or getattr(obj, f"_fetch_{attr}", None) + fetcher = getattr(obj, f"fetch_{attr}", None) or getattr( + obj, f"_fetch_{attr}", None + ) if fetcher: try: return await fetcher(object_id) @@ -662,6 +666,7 @@ async def get_or_fetch( return default raise + def _unique(iterable: Iterable[T]) -> list[T]: return [x for x in dict.fromkeys(iterable)] From c0689cc2369e94cf7fb9ea76bc031cbcba882225 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 01:30:24 +0300 Subject: [PATCH 007/111] usage of subclass Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 0d0b2c764d..83fa8ef3db 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -633,16 +633,9 @@ async def get_or_fetch( Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] The object of type that was specified or ``None`` if not found. """ - if object_type.__name__ in {"Member", "User", "Guild"}: + if issubclass(object_type, (Member, User, Guild)): attr = object_type.__name__.lower() - elif object_type.__name__ in { - "VoiceChannel", - "TextChannel", - "ForumChannel", - "StageChannel", - "CategoryChannel", - "Thread", - }: + elif issubclass(object_type, (VoiceChannel, TextChannel, ForumChannel, StageChannel, CategoryChannel, Thread)): attr = "channel" else: raise InvalidArgument( From b20822a97bc849070599469a67fd066818955305 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 22:30:50 +0000 Subject: [PATCH 008/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/guild.py | 1 - discord/utils.py | 14 +++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index c5d898328d..91fae839de 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -36,7 +36,6 @@ Optional, Sequence, Tuple, - Type, TypeVar, Union, overload, diff --git a/discord/utils.py b/discord/utils.py index 83fa8ef3db..fcb505f363 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -56,10 +56,8 @@ Iterator, Literal, Mapping, - Optional, Protocol, Sequence, - Type, TypeVar, Union, overload, @@ -635,7 +633,17 @@ async def get_or_fetch( """ if issubclass(object_type, (Member, User, Guild)): attr = object_type.__name__.lower() - elif issubclass(object_type, (VoiceChannel, TextChannel, ForumChannel, StageChannel, CategoryChannel, Thread)): + elif issubclass( + object_type, + ( + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + ), + ): attr = "channel" else: raise InvalidArgument( From 4a363f5aaed983057f698215f44c43925fdd38a6 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 01:41:58 +0300 Subject: [PATCH 009/111] add get_emoji method Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 91fae839de..d844660b94 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -874,6 +874,7 @@ def get_member(self, user_id: int, /) -> Member | None: CategoryChannel, Thread, Member, + GuildEmoji, ], ) @@ -887,7 +888,7 @@ async def get_or_fetch( Parameters ---------- - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`] Type of object to fetch or get. object_id: :class:`int` @@ -899,7 +900,7 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] The object of type that was specified or ``None`` if not found. """ return await utils.get_or_fetch( @@ -2707,6 +2708,26 @@ async def delete_sticker( """ await self._state.http.delete_guild_sticker(self.id, sticker.id, reason) + def get_emoji(self, emoji_id: int, /) -> Optional[GuildEmoji]: + """Returns an emoji with the given ID. + + .. versionadded:: 2.7 + + Parameters + ---------- + emoji_id: int + The ID to search for. + + Returns + -------- + Optional[:class:`Emoji`] + The returned Emoji or ``None`` if not found. + """ + emoji = self._state.get_emoji(emoji_id) + if emoji and emoji.guild == self: + return emoji + return None + async def fetch_emojis(self) -> list[GuildEmoji]: r"""|coro| From c3a7dd2b3264bb7fd639a724223343d122e7e565 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 22:42:23 +0000 Subject: [PATCH 010/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/guild.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index d844660b94..a8e80d0b0f 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -2708,7 +2708,7 @@ async def delete_sticker( """ await self._state.http.delete_guild_sticker(self.id, sticker.id, reason) - def get_emoji(self, emoji_id: int, /) -> Optional[GuildEmoji]: + def get_emoji(self, emoji_id: int, /) -> GuildEmoji | None: """Returns an emoji with the given ID. .. versionadded:: 2.7 @@ -2719,7 +2719,7 @@ def get_emoji(self, emoji_id: int, /) -> Optional[GuildEmoji]: The ID to search for. Returns - -------- + ------- Optional[:class:`Emoji`] The returned Emoji or ``None`` if not found. """ @@ -2727,7 +2727,7 @@ def get_emoji(self, emoji_id: int, /) -> Optional[GuildEmoji]: if emoji and emoji.guild == self: return emoji return None - + async def fetch_emojis(self) -> list[GuildEmoji]: r"""|coro| From e614c479af5dc0c9fc689886d8c02ab58fddb764 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 01:43:58 +0300 Subject: [PATCH 011/111] add get_or_fetch_emoji method Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index fcb505f363..7dfd2540c9 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -75,6 +75,7 @@ Member, User, Guild, + GuildEmoji, ) from .errors import HTTPException, InvalidArgument @@ -599,6 +600,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: Member, User, Guild, + GuildEmoji, ], ) @@ -616,7 +618,7 @@ async def get_or_fetch( ---------- obj : Guild | Client The object to operate on. - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`] Type of object to fetch or get. object_id: :class:`int` @@ -628,10 +630,10 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] The object of type that was specified or ``None`` if not found. """ - if issubclass(object_type, (Member, User, Guild)): + if issubclass(object_type, (Member, User, Guild, GuildEmoji)): attr = object_type.__name__.lower() elif issubclass( object_type, From 0c0a042e377bdf441dea1e5d8cd66fc40b6103d5 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 18:22:30 +0300 Subject: [PATCH 012/111] shortcut getorfetch client + removal of get_or_fetch user in favor of get_or_fetch shortcut Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 56 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/discord/client.py b/discord/client.py index 6768d4a660..f08d2f844d 100644 --- a/discord/client.py +++ b/discord/client.py @@ -67,11 +67,19 @@ if TYPE_CHECKING: from .abc import GuildChannel, PrivateChannel, Snowflake, SnowflakeTime - from .channel import DMChannel + from .channel import ( + DMChannel, + CategoryChannel, + ForumChannel, + StageChannel, + TextChannel, + VoiceChannel, + ) from .member import Member from .message import Message from .poll import Poll from .voice_client import VoiceProtocol + from .threads import Thread, ThreadMember __all__ = ("Client",) @@ -1113,24 +1121,44 @@ def get_all_members(self) -> Generator[Member]: for guild in self.guilds: yield from guild.members - async def get_or_fetch_user(self, id: int, /) -> User | None: - """|coro| - - Looks up a user in the user cache or fetches if not found. - + _FETCHABLE = TypeVar( + "_FETCHABLE", + bound=Union[ + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + User, + GuildEmoji, + Guild, + ], + ) + + async def get_or_fetch( + self: Client, + object_type: type[_FETCHABLE], + object_id: int, + default: Any = MISSING, + ) -> _FETCHABLE | None: + """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. Parameters ---------- - id: :class:`int` - The ID to search for. - + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`] + Type of object to fetch or get. + object_id: :class:`int` + ID of object to get. + default : Any, optional + A default to return instead of raising if fetch fails. Returns ------- - Optional[:class:`~discord.User`] - The user or ``None`` if not found. + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`]] + The object of type that was specified or ``None`` if not found. """ - - return await utils.get_or_fetch(obj=self, attr="user", id=id, default=None) - + return await utils.get_or_fetch( + obj=self, object_type=object_type, object_id=object_id, default=default + ) # listeners/waiters async def wait_until_ready(self) -> None: From 466e41b918b5bcf010fc633eee4837fe8391fb1a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 15:22:56 +0000 Subject: [PATCH 013/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/discord/client.py b/discord/client.py index f08d2f844d..5ac191a587 100644 --- a/discord/client.py +++ b/discord/client.py @@ -68,8 +68,8 @@ if TYPE_CHECKING: from .abc import GuildChannel, PrivateChannel, Snowflake, SnowflakeTime from .channel import ( - DMChannel, CategoryChannel, + DMChannel, ForumChannel, StageChannel, TextChannel, @@ -78,8 +78,8 @@ from .member import Member from .message import Message from .poll import Poll - from .voice_client import VoiceProtocol from .threads import Thread, ThreadMember + from .voice_client import VoiceProtocol __all__ = ("Client",) @@ -1143,6 +1143,7 @@ async def get_or_fetch( default: Any = MISSING, ) -> _FETCHABLE | None: """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. + Parameters ---------- object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`] @@ -1151,6 +1152,7 @@ async def get_or_fetch( ID of object to get. default : Any, optional A default to return instead of raising if fetch fails. + Returns ------- Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`]] @@ -1159,6 +1161,7 @@ async def get_or_fetch( return await utils.get_or_fetch( obj=self, object_type=object_type, object_id=object_id, default=default ) + # listeners/waiters async def wait_until_ready(self) -> None: From 388a23478ecd19fb24cce434411e4801eea7346b Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 18:55:19 +0300 Subject: [PATCH 014/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 7dfd2540c9..b23735bd74 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -590,21 +590,9 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound=Union[ - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - Member, - User, - Guild, - GuildEmoji, - ], + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji" ) - async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], From 9737f693c0dda1c64a1870c6b031900ab51b77db Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 18:55:47 +0300 Subject: [PATCH 015/111] Update guild.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index a8e80d0b0f..ba737941a1 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -864,18 +864,10 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) + _FETCHABLE = TypeVar( "_FETCHABLE", - bound=Union[ - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - Member, - GuildEmoji, - ], + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | GuildEmoji" ) async def get_or_fetch( From 656de1445f13cb5c43716d2289fccb7cd586ac91 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 18:56:01 +0300 Subject: [PATCH 016/111] Update client.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/discord/client.py b/discord/client.py index 5ac191a587..3649becae1 100644 --- a/discord/client.py +++ b/discord/client.py @@ -31,7 +31,7 @@ import sys import traceback from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar, Union import aiohttp @@ -1121,19 +1121,10 @@ def get_all_members(self) -> Generator[Member]: for guild in self.guilds: yield from guild.members + _FETCHABLE = TypeVar( "_FETCHABLE", - bound=Union[ - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - User, - GuildEmoji, - Guild, - ], + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji" ) async def get_or_fetch( From 461eab79f9fe9f48fc6a1af2ddc1d4d7f1e172e9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 15:56:16 +0000 Subject: [PATCH 017/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/guild.py | 3 +-- discord/utils.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index ba737941a1..c805463592 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -864,10 +864,9 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) - _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | GuildEmoji" + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | GuildEmoji", ) async def get_or_fetch( diff --git a/discord/utils.py b/discord/utils.py index b23735bd74..8420ed4e1b 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -590,9 +590,10 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji" + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji", ) + async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], From b6f88788777b1cdc26806871fffde983d19f1287 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 18:59:24 +0300 Subject: [PATCH 018/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 8420ed4e1b..7a0c8ebab4 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -590,10 +590,9 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji", + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji" ) - async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], @@ -622,6 +621,10 @@ async def get_or_fetch( Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] The object of type that was specified or ``None`` if not found. """ + from discord import ( + VoiceChannel, TextChannel, ForumChannel, StageChannel, + CategoryChannel, Thread, Member, User, Guild, GuildEmoji + ) if issubclass(object_type, (Member, User, Guild, GuildEmoji)): attr = object_type.__name__.lower() elif issubclass( From f2a7d72a73937c20f1b8408379c0176a2cf70a20 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 15:59:50 +0000 Subject: [PATCH 019/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 5 ++--- discord/utils.py | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/discord/client.py b/discord/client.py index 3649becae1..cc341a2066 100644 --- a/discord/client.py +++ b/discord/client.py @@ -31,7 +31,7 @@ import sys import traceback from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar import aiohttp @@ -1121,10 +1121,9 @@ def get_all_members(self) -> Generator[Member]: for guild in self.guilds: yield from guild.members - _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji" + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji", ) async def get_or_fetch( diff --git a/discord/utils.py b/discord/utils.py index 7a0c8ebab4..364fcdabd4 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -590,9 +590,10 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji" + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji", ) + async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], @@ -622,9 +623,18 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( - VoiceChannel, TextChannel, ForumChannel, StageChannel, - CategoryChannel, Thread, Member, User, Guild, GuildEmoji + CategoryChannel, + ForumChannel, + Guild, + GuildEmoji, + Member, + StageChannel, + TextChannel, + Thread, + User, + VoiceChannel, ) + if issubclass(object_type, (Member, User, Guild, GuildEmoji)): attr = object_type.__name__.lower() elif issubclass( From 5d682071f51cd59a65a578dde078cb79987fbbab Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 21:46:54 +0300 Subject: [PATCH 020/111] add utils.deprecated Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/discord/client.py b/discord/client.py index cc341a2066..b1895eb373 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1121,6 +1121,26 @@ def get_all_members(self) -> Generator[Member]: for guild in self.guilds: yield from guild.members + @utils.deprecated( + instead="Client.get_or_fetch(User, id)", + since="2.7", + ) + async def get_or_fetch_user(self, id: int, /) -> User | None: + """|coro| + + Looks up a user in the user cache or fetches if not found. + Parameters + ---------- + id: :class:`int` + The ID to search for. + Returns + ------- + Optional[:class:`~discord.User`] + The user or ``None`` if not found. + """ + + return await utils.get_or_fetch(obj=self, attr=User, id=id, default=None) + _FETCHABLE = TypeVar( "_FETCHABLE", bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji", From 4ca87d8efb076a5b404fdd397183e66e7eef8e34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 18:47:21 +0000 Subject: [PATCH 021/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index b1895eb373..4fda67423e 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1129,10 +1129,12 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: """|coro| Looks up a user in the user cache or fetches if not found. + Parameters ---------- id: :class:`int` The ID to search for. + Returns ------- Optional[:class:`~discord.User`] @@ -1140,7 +1142,7 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: """ return await utils.get_or_fetch(obj=self, attr=User, id=id, default=None) - + _FETCHABLE = TypeVar( "_FETCHABLE", bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji", From 9903fb73c8b77d3eb1bfb0597f7c15297264c75a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 22:01:53 +0300 Subject: [PATCH 022/111] add warning Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 73 +++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 364fcdabd4..2cde4f3775 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -590,10 +590,9 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji", + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", ) - async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], @@ -607,7 +606,7 @@ async def get_or_fetch( ---------- obj : Guild | Client The object to operate on. - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`] Type of object to fetch or get. object_id: :class:`int` @@ -619,40 +618,50 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`]] The object of type that was specified or ``None`` if not found. """ from discord import ( - CategoryChannel, - ForumChannel, - Guild, - GuildEmoji, - Member, - StageChannel, - TextChannel, - Thread, - User, - VoiceChannel, + VoiceChannel, TextChannel, ForumChannel, StageChannel, + CategoryChannel, Thread, Member, User, Guild, GuildEmoji, AppEmoji ) - - if issubclass(object_type, (Member, User, Guild, GuildEmoji)): - attr = object_type.__name__.lower() - elif issubclass( - object_type, - ( - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - ), - ): - attr = "channel" - else: - raise InvalidArgument( - f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + if isinstance(object_type, str): + warn_deprecated( + name="get_or_fetch(obj, attr=str, ...)", + instead="get_or_fetch(obj, object_type=Type, ...)", + since="2.7", ) + if object_type not in ["emoji", "channel", "member", "user", "guild"]: + raise InvalidArgument( + f"Invalid type {object_type} passed to get_or_fetch." + ) + else: + attr = object_type + else: + if issubclass(object_type, (Member, User, Guild)): + attr = object_type.__name__.lower() + elif issubclass(object_type, (GuildEmoji, AppEmoji)): + attr = "emoji" + elif issubclass( + object_type, + ( + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + ), + ): + attr = "channel" + else: + raise InvalidArgument( + f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + ) + if isinstance(obj, Guild) and object_type is User: + raise InvalidArgument("Guild cannot get_or_fetch discord.User. Use Client instead.") + elif isinstance(obj, Client) and object_type is Member: + raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") getter = getattr(obj, f"get_{attr}", None) if getter: From a1ec6f91d340dbc3e1688935bfc2388eda76e015 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 4 May 2025 19:02:19 +0000 Subject: [PATCH 023/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 2cde4f3775..3460ad3187 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -593,6 +593,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", ) + async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], @@ -622,9 +623,19 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( - VoiceChannel, TextChannel, ForumChannel, StageChannel, - CategoryChannel, Thread, Member, User, Guild, GuildEmoji, AppEmoji + AppEmoji, + CategoryChannel, + ForumChannel, + Guild, + GuildEmoji, + Member, + StageChannel, + TextChannel, + Thread, + User, + VoiceChannel, ) + if isinstance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr=str, ...)", @@ -632,9 +643,7 @@ async def get_or_fetch( since="2.7", ) if object_type not in ["emoji", "channel", "member", "user", "guild"]: - raise InvalidArgument( - f"Invalid type {object_type} passed to get_or_fetch." - ) + raise InvalidArgument(f"Invalid type {object_type} passed to get_or_fetch.") else: attr = object_type else: @@ -659,9 +668,11 @@ async def get_or_fetch( f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" ) if isinstance(obj, Guild) and object_type is User: - raise InvalidArgument("Guild cannot get_or_fetch discord.User. Use Client instead.") + raise InvalidArgument( + "Guild cannot get_or_fetch discord.User. Use Client instead." + ) elif isinstance(obj, Client) and object_type is Member: - raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") + raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") getter = getattr(obj, f"get_{attr}", None) if getter: From 47de080c6575f9d9f347fae717033b9681a5d8db Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 4 May 2025 22:04:19 +0300 Subject: [PATCH 024/111] Update client.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 4fda67423e..cb7aa0d5cd 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1145,7 +1145,7 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji", + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji", ) async def get_or_fetch( @@ -1158,7 +1158,7 @@ async def get_or_fetch( Parameters ---------- - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`GuildEmoji`, :class:`AppEmoji`] Type of object to fetch or get. object_id: :class:`int` ID of object to get. @@ -1167,7 +1167,7 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`GuildEmoji`, :class:`Guild`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`GuildEmoji`, :class:`AppEmoji`]] The object of type that was specified or ``None`` if not found. """ return await utils.get_or_fetch( From 443082527c9cbeb9d6b22af016abcf19665664a1 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:17:49 +0300 Subject: [PATCH 025/111] added backward support Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 80 +++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 3460ad3187..012cad2b7d 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -593,12 +593,14 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", ) - +# TODO REMOVE THE MISSING FOR object_type and object_id + remove both attr and id after depreciation async def get_or_fetch( obj: Guild | Client, - object_type: type[_FETCHABLE], - object_id: int, + object_type: type[_FETCHABLE] = MISSING, + object_id: int = MISSING, default: Any = MISSING, + attr: str = MISSING + id: int = MISSING, ) -> _FETCHABLE | None: """ Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. @@ -635,38 +637,54 @@ async def get_or_fetch( User, VoiceChannel, ) - - if isinstance(object_type, str): + # TODO REMOVE THIS PART AfTER DEPREcIATION + string_to_type = { + "channel": TextChannel, + "member": Member, + "user": User, + "guild": Guild, + "emoji": GuildEmoji, + "appemoji": AppEmoji, + } + + if attr is not MISSING and id is not MISSING: warn_deprecated( - name="get_or_fetch(obj, attr=str, ...)", - instead="get_or_fetch(obj, object_type=Type, ...)", + name="get_or_fetch(obj, attr='type', id=...)", + instead="get_or_fetch(obj, object_type=Type, object_id=...)", since="2.7", + removed="3.0", + reference="https://github.com/Lumabots/pycord/pull/XYZ" ) - if object_type not in ["emoji", "channel", "member", "user", "guild"]: - raise InvalidArgument(f"Invalid type {object_type} passed to get_or_fetch.") - else: - attr = object_type + mapped_type = string_to_type.get(attr.lower()) + if mapped_type is None: + raise InvalidArgument(f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead.") + object_type = mapped_type + object_id = id + + if object_type is MISSING or object_id is MISSING: + raise TypeError("Missing required parameters: `object_type` and `object_id`.") + # Util here + + if issubclass(object_type, (Member, User, Guild)): + attr = object_type.__name__.lower() + elif issubclass(object_type, (GuildEmoji, AppEmoji)): + attr = "emoji" + elif issubclass( + object_type, + ( + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + ), + ): + attr = "channel" else: - if issubclass(object_type, (Member, User, Guild)): - attr = object_type.__name__.lower() - elif issubclass(object_type, (GuildEmoji, AppEmoji)): - attr = "emoji" - elif issubclass( - object_type, - ( - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - ), - ): - attr = "channel" - else: - raise InvalidArgument( - f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" - ) + raise InvalidArgument( + f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + ) if isinstance(obj, Guild) and object_type is User: raise InvalidArgument( "Guild cannot get_or_fetch discord.User. Use Client instead." From 010cc78529c443ec68fd6ab9e582ba0daac1fc20 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:18:24 +0000 Subject: [PATCH 026/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/utils.py b/discord/utils.py index 012cad2b7d..f3b592ab66 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -637,6 +637,7 @@ async def get_or_fetch( User, VoiceChannel, ) + # TODO REMOVE THIS PART AfTER DEPREcIATION string_to_type = { "channel": TextChannel, From 368a55d8f6b787ec791b4a4ba1fec7dfba8c7299 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:20:29 +0300 Subject: [PATCH 027/111] fixed missing coma Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index f3b592ab66..3ba18925fa 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -130,9 +130,9 @@ def __repr__(self) -> str: MISSING: Any = _MissingSentinel() -# As of 3.11, directly setting a dataclass field to MISSING causes a ValueError. Using +# As of 3.11, directly setting a dataclass field to causes a ValueError. Using # field(default=MISSING) produces the same error, but passing a lambda to -# default_factory produces the same behavior as default=MISSING and does not raise an +# default_factory produces the same behavior as default=and does not raise an # error. MissingField = field(default_factory=lambda: MISSING) @@ -593,13 +593,13 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", ) -# TODO REMOVE THE MISSING FOR object_type and object_id + remove both attr and id after depreciation +# TODO REMOVE THE FOR object_type and object_id + remove both attr and id after depreciation async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, object_id: int = MISSING, default: Any = MISSING, - attr: str = MISSING + attr: str = MISSING, id: int = MISSING, ) -> _FETCHABLE | None: """ @@ -648,13 +648,11 @@ async def get_or_fetch( "appemoji": AppEmoji, } - if attr is not MISSING and id is not MISSING: + if attr is not and id is not MISSING or isistance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr='type', id=...)", instead="get_or_fetch(obj, object_type=Type, object_id=...)", since="2.7", - removed="3.0", - reference="https://github.com/Lumabots/pycord/pull/XYZ" ) mapped_type = string_to_type.get(attr.lower()) if mapped_type is None: @@ -662,8 +660,8 @@ async def get_or_fetch( object_type = mapped_type object_id = id - if object_type is MISSING or object_id is MISSING: - raise TypeError("Missing required parameters: `object_type` and `object_id`.") + if object_type is or object_id is MISSING: + raise TypeError("required parameters: `object_type` and `object_id`.") # Util here if issubclass(object_type, (Member, User, Guild)): From 76c56a81b50437f10685c4b5b0885c67861dfcd0 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:22:11 +0300 Subject: [PATCH 028/111] ig im drunk Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 3ba18925fa..40bae9b5ed 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -648,7 +648,7 @@ async def get_or_fetch( "appemoji": AppEmoji, } - if attr is not and id is not MISSING or isistance(object_type, str): + if attr is not MISSING or id is not MISSING or isistance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr='type', id=...)", instead="get_or_fetch(obj, object_type=Type, object_id=...)", From 811682d360b38235de14dc134c509290690e32de Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:29:17 +0300 Subject: [PATCH 029/111] fix my drunkness Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 40bae9b5ed..525bb5f6ba 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -130,9 +130,9 @@ def __repr__(self) -> str: MISSING: Any = _MissingSentinel() -# As of 3.11, directly setting a dataclass field to causes a ValueError. Using +# As of 3.11, directly setting a dataclass field to MISSING causes a ValueError. Using # field(default=MISSING) produces the same error, but passing a lambda to -# default_factory produces the same behavior as default=and does not raise an +# default_factory produces the same behavior as default=MISSING and does not raise an # error. MissingField = field(default_factory=lambda: MISSING) @@ -660,7 +660,7 @@ async def get_or_fetch( object_type = mapped_type object_id = id - if object_type is or object_id is MISSING: + if object_type is MISSING or object_id is MISSING: raise TypeError("required parameters: `object_type` and `object_id`.") # Util here From a102d5d0969ef0baf46070d9d88cbc16e3c6ca42 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:29:50 +0000 Subject: [PATCH 030/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 525bb5f6ba..31fcba29e1 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -593,6 +593,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", ) + # TODO REMOVE THE FOR object_type and object_id + remove both attr and id after depreciation async def get_or_fetch( obj: Guild | Client, @@ -656,7 +657,9 @@ async def get_or_fetch( ) mapped_type = string_to_type.get(attr.lower()) if mapped_type is None: - raise InvalidArgument(f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead.") + raise InvalidArgument( + f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead." + ) object_type = mapped_type object_id = id From 528b84ba960f20d8e8e8fb57bf72324816ee3636 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:33:22 +0300 Subject: [PATCH 031/111] now im not drunk Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 31fcba29e1..099e2212ec 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -649,7 +649,7 @@ async def get_or_fetch( "appemoji": AppEmoji, } - if attr is not MISSING or id is not MISSING or isistance(object_type, str): + if attr is not MISSING or id is not MISSING or isinstance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr='type', id=...)", instead="get_or_fetch(obj, object_type=Type, object_id=...)", From 7e766be53324a6a8242dbdcaccfcbac67e880e95 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:35:22 +0300 Subject: [PATCH 032/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 099e2212ec..0a6588d37d 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -655,7 +655,8 @@ async def get_or_fetch( instead="get_or_fetch(obj, object_type=Type, object_id=...)", since="2.7", ) - mapped_type = string_to_type.get(attr.lower()) + attr = object_type if object_type is not MISSING else attr + mapped_type = string_to_type.get(attr.lower() ) if mapped_type is None: raise InvalidArgument( f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead." From f6b8e18cd691cb203f4be9896d2606bbbc60810a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:36:42 +0000 Subject: [PATCH 033/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 0a6588d37d..aeb32900bf 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -656,7 +656,7 @@ async def get_or_fetch( since="2.7", ) attr = object_type if object_type is not MISSING else attr - mapped_type = string_to_type.get(attr.lower() ) + mapped_type = string_to_type.get(attr.lower()) if mapped_type is None: raise InvalidArgument( f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead." From a5deffed402ace62be36d8ba3623bc0bfdfec393 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:43:46 +0300 Subject: [PATCH 034/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index aeb32900bf..6013e5d72f 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -655,14 +655,25 @@ async def get_or_fetch( instead="get_or_fetch(obj, object_type=Type, object_id=...)", since="2.7", ) - attr = object_type if object_type is not MISSING else attr - mapped_type = string_to_type.get(attr.lower()) - if mapped_type is None: - raise InvalidArgument( - f"Unknown type string '{attr}' passed as `attr`. Use a valid object class instead." + + deprecated_attr = attr if attr is not MISSING else object_type + deprecated_id = id if id is not MISSING else object_id + + if isinstance(deprecated_attr, str): + mapped_type = string_to_type.get(deprecated_attr.lower()) + if mapped_type is None: + raise InvalidArgument( + f"Unknown type string '{deprecated_attr}' used. Please use a valid object class like `discord.Member` instead." + ) + object_type = mapped_type + elif isinstance(deprecated_attr, type): + object_type = deprecated_attr + else: + raise TypeError( + f"Invalid `attr` or `object_type`: expected a string or class, got {type(deprecated_attr).__name__}." ) - object_type = mapped_type - object_id = id + + object_id = deprecated_id if object_type is MISSING or object_id is MISSING: raise TypeError("required parameters: `object_type` and `object_id`.") From fff74c43bd2a963895a7982f308cf18a64dd7346 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 12:47:38 +0300 Subject: [PATCH 035/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 6013e5d72f..65c62e8000 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -626,19 +626,19 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( - AppEmoji, - CategoryChannel, - ForumChannel, - Guild, - GuildEmoji, - Member, - StageChannel, - TextChannel, - Thread, - User, - VoiceChannel, - ) - + Client, + VoiceChannel, + TextChannel, + ForumChannel, + StageChannel, + CategoryChannel, + Thread, + Member, + User, + Guild, + GuildEmoji, + AppEmoji, + ) # TODO REMOVE THIS PART AfTER DEPREcIATION string_to_type = { "channel": TextChannel, From 9e456273f0e92cc0058a20e7c4c3a298d1738e84 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:49:22 +0000 Subject: [PATCH 036/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 65c62e8000..213e06a6fd 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -626,19 +626,20 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( - Client, - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - Member, - User, - Guild, - GuildEmoji, - AppEmoji, - ) + AppEmoji, + CategoryChannel, + Client, + ForumChannel, + Guild, + GuildEmoji, + Member, + StageChannel, + TextChannel, + Thread, + User, + VoiceChannel, + ) + # TODO REMOVE THIS PART AfTER DEPREcIATION string_to_type = { "channel": TextChannel, From 0baae1d549f383ebdabe6806eac1f0456946b00f Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 14:17:38 +0300 Subject: [PATCH 037/111] usage of abc Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 213e06a6fd..b7f65bfbf6 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -626,18 +626,14 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( - AppEmoji, - CategoryChannel, Client, - ForumChannel, + User, Guild, + AppEmoji, GuildEmoji, Member, - StageChannel, - TextChannel, - Thread, - User, - VoiceChannel, + abc, + ) # TODO REMOVE THIS PART AfTER DEPREcIATION @@ -684,17 +680,7 @@ async def get_or_fetch( attr = object_type.__name__.lower() elif issubclass(object_type, (GuildEmoji, AppEmoji)): attr = "emoji" - elif issubclass( - object_type, - ( - VoiceChannel, - TextChannel, - ForumChannel, - StageChannel, - CategoryChannel, - Thread, - ), - ): + elif issubclass(object_type, abc.GuildChannel): attr = "channel" else: raise InvalidArgument( From a3aeb4bb7bdc74a6310c3795f67a3ba4fa759042 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 11:18:24 +0000 Subject: [PATCH 038/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index b7f65bfbf6..945145d0f2 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -626,14 +626,13 @@ async def get_or_fetch( The object of type that was specified or ``None`` if not found. """ from discord import ( + AppEmoji, Client, - User, Guild, - AppEmoji, GuildEmoji, Member, + User, abc, - ) # TODO REMOVE THIS PART AfTER DEPREcIATION From 2404d9cf8ab904555f09f58ff267a8688dd1663d Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 14:20:17 +0300 Subject: [PATCH 039/111] _EmojiTag Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 945145d0f2..a1a8ab57aa 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -633,15 +633,16 @@ async def get_or_fetch( Member, User, abc, + _EmojiTag, ) # TODO REMOVE THIS PART AfTER DEPREcIATION string_to_type = { - "channel": TextChannel, + "channel": abc.GuildChannel, "member": Member, "user": User, "guild": Guild, - "emoji": GuildEmoji, + "emoji": _EmojiTag, "appemoji": AppEmoji, } @@ -677,7 +678,7 @@ async def get_or_fetch( if issubclass(object_type, (Member, User, Guild)): attr = object_type.__name__.lower() - elif issubclass(object_type, (GuildEmoji, AppEmoji)): + elif issubclass(object_type, _EmojiTag): attr = "emoji" elif issubclass(object_type, abc.GuildChannel): attr = "channel" From 23409a7e17a7a1495dbc52d0948db28da85bf484 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 11:20:44 +0000 Subject: [PATCH 040/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index a1a8ab57aa..bb1709268a 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -632,8 +632,8 @@ async def get_or_fetch( GuildEmoji, Member, User, - abc, _EmojiTag, + abc, ) # TODO REMOVE THIS PART AfTER DEPREcIATION From 8f278f8fefac65f660462585f06833893946dc0f Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Mon, 5 May 2025 14:22:21 +0300 Subject: [PATCH 041/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index bb1709268a..07b1468139 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -632,7 +632,7 @@ async def get_or_fetch( GuildEmoji, Member, User, - _EmojiTag, + emoji, abc, ) @@ -642,7 +642,7 @@ async def get_or_fetch( "member": Member, "user": User, "guild": Guild, - "emoji": _EmojiTag, + "emoji": emoji._EmojiTag, "appemoji": AppEmoji, } @@ -678,7 +678,7 @@ async def get_or_fetch( if issubclass(object_type, (Member, User, Guild)): attr = object_type.__name__.lower() - elif issubclass(object_type, _EmojiTag): + elif issubclass(object_type, emoji._EmojiTag): attr = "emoji" elif issubclass(object_type, abc.GuildChannel): attr = "channel" From 6d5be54cedbc0341865ea86048eff7f1913adfb2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 11:22:45 +0000 Subject: [PATCH 042/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 07b1468139..2d5a06f71a 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -632,8 +632,8 @@ async def get_or_fetch( GuildEmoji, Member, User, - emoji, abc, + emoji, ) # TODO REMOVE THIS PART AfTER DEPREcIATION From 11fbbc9d8c11679fc316d8868548def3b5197bd1 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 07:18:30 +0300 Subject: [PATCH 043/111] Update utils.py Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 2d5a06f71a..636f6afb64 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -594,7 +594,6 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: ) -# TODO REMOVE THE FOR object_type and object_id + remove both attr and id after depreciation async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, @@ -636,7 +635,6 @@ async def get_or_fetch( emoji, ) - # TODO REMOVE THIS PART AfTER DEPREcIATION string_to_type = { "channel": abc.GuildChannel, "member": Member, @@ -674,7 +672,6 @@ async def get_or_fetch( if object_type is MISSING or object_id is MISSING: raise TypeError("required parameters: `object_type` and `object_id`.") - # Util here if issubclass(object_type, (Member, User, Guild)): attr = object_type.__name__.lower() From 267d5745fa79f54f05bdce6c31e74319463ba83a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 14:46:18 +0300 Subject: [PATCH 044/111] Update discord/client.py Co-authored-by: plun1331 Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index cb7aa0d5cd..5c52759000 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1141,7 +1141,7 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: The user or ``None`` if not found. """ - return await utils.get_or_fetch(obj=self, attr=User, id=id, default=None) + return await self.get_or_fetch(object_type=User, object_id=id, default=None) _FETCHABLE = TypeVar( "_FETCHABLE", From dc454499041f34578d4866ffc2705f7b442e6971 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 14:53:11 +0300 Subject: [PATCH 045/111] add the raise error Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/discord/utils.py b/discord/utils.py index 636f6afb64..7baaef5ae3 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -623,6 +623,15 @@ async def get_or_fetch( Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`]] The object of type that was specified or ``None`` if not found. + + Raises + ------ + :exc:`NotFound` + Invalid ID for the object + :exc:`HTTPException` + An error occurred fetching the object + :exc:`Forbidden` + You do not have permission to fetch the object """ from discord import ( AppEmoji, From f22d8a226c2fbc5544dfcb74af8a0526c112c7da Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 14:53:43 +0300 Subject: [PATCH 046/111] Update discord/utils.py Co-authored-by: plun1331 Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 7baaef5ae3..818521b40f 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -603,7 +603,7 @@ async def get_or_fetch( id: int = MISSING, ) -> _FETCHABLE | None: """ - Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. + Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. Parameters ---------- From cbb79511aededb93a1d35602205d9844f92b7b42 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 14:56:30 +0300 Subject: [PATCH 047/111] Update utils.pyfix missing appemoji Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/utils.py b/discord/utils.py index 818521b40f..270a1dd9f1 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -76,6 +76,7 @@ User, Guild, GuildEmoji, + AppEmoji ) from .errors import HTTPException, InvalidArgument From fdbb7d36ada903d8d39874d8b777fae6b2c9b6c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 11:58:06 +0000 Subject: [PATCH 048/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 270a1dd9f1..e8311f78ad 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -76,7 +76,7 @@ User, Guild, GuildEmoji, - AppEmoji + AppEmoji, ) from .errors import HTTPException, InvalidArgument From 5b35d95ee5a9318e796c5dcf8a05ba5084a4a8ec Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 15:42:59 +0300 Subject: [PATCH 049/111] add missing raise to doc --- discord/client.py | 12 +++++++++++- discord/guild.py | 12 +++++++++++- discord/utils.py | 1 - 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 5c52759000..187162087a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1154,7 +1154,8 @@ async def get_or_fetch( object_id: int, default: Any = MISSING, ) -> _FETCHABLE | None: - """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. + """ + Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. Parameters ---------- @@ -1169,6 +1170,15 @@ async def get_or_fetch( ------- Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`GuildEmoji`, :class:`AppEmoji`]] The object of type that was specified or ``None`` if not found. + + Raises + ------ + :exc:`NotFound` + Invalid ID for the object + :exc:`HTTPException` + An error occurred fetching the object + :exc:`Forbidden` + You do not have permission to fetch the object """ return await utils.get_or_fetch( obj=self, object_type=object_type, object_id=object_id, default=default diff --git a/discord/guild.py b/discord/guild.py index c805463592..8b38b02ce4 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -875,7 +875,8 @@ async def get_or_fetch( object_id: int, default: Any = MISSING, ) -> _FETCHABLE | None: - """Shortcut method to get data from guild object either by returning the cached version, or if it does not exist, attempt to fetch it from the api. + """ + Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. Parameters ---------- @@ -893,6 +894,15 @@ async def get_or_fetch( Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] The object of type that was specified or ``None`` if not found. + + Raises + ------ + :exc:`NotFound` + Invalid ID for the object + :exc:`HTTPException` + An error occurred fetching the object + :exc:`Forbidden` + You do not have permission to fetch the object """ return await utils.get_or_fetch( obj=self, object_type=object_type, object_id=object_id, default=default diff --git a/discord/utils.py b/discord/utils.py index e8311f78ad..044a25da1e 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -638,7 +638,6 @@ async def get_or_fetch( AppEmoji, Client, Guild, - GuildEmoji, Member, User, abc, From b8a8628f40206178e64369398727e60a7001959a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 22:08:48 +0300 Subject: [PATCH 050/111] refactor: add TODO comments for removal of deprecated arguments in get_or_fetch_user and get_or_fetch --- discord/client.py | 3 ++- discord/utils.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/discord/client.py b/discord/client.py index 187162087a..16818213bc 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1124,8 +1124,9 @@ def get_all_members(self) -> Generator[Member]: @utils.deprecated( instead="Client.get_or_fetch(User, id)", since="2.7", + removed="3.0", ) - async def get_or_fetch_user(self, id: int, /) -> User | None: + async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in 3.0 """|coro| Looks up a user in the user cache or fetches if not found. diff --git a/discord/utils.py b/discord/utils.py index 044a25da1e..ae49a8e1ba 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -602,7 +602,7 @@ async def get_or_fetch( default: Any = MISSING, attr: str = MISSING, id: int = MISSING, -) -> _FETCHABLE | None: +) -> _FETCHABLE | None: # TODO: Remove in 3.0 the arguments attr and id """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. From ba5541ee8874d28ef0df10bd9f0560b8d6f82d20 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 22:55:10 +0300 Subject: [PATCH 051/111] refactor: move _FETCHABLE type variable to utils for better accessibility --- discord/client.py | 7 +------ discord/guild.py | 8 ++------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/discord/client.py b/discord/client.py index 16818213bc..c91b5e3f28 100644 --- a/discord/client.py +++ b/discord/client.py @@ -60,7 +60,7 @@ from .threads import Thread from .ui.view import View from .user import ClientUser, User -from .utils import MISSING +from .utils import _FETCHABLE, MISSING from .voice_client import VoiceClient from .webhook import Webhook from .widget import Widget @@ -1144,11 +1144,6 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in return await self.get_or_fetch(object_type=User, object_id=id, default=None) - _FETCHABLE = TypeVar( - "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji", - ) - async def get_or_fetch( self: Client, object_type: type[_FETCHABLE], diff --git a/discord/guild.py b/discord/guild.py index 8b38b02ce4..99090bf35c 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -36,11 +36,12 @@ Optional, Sequence, Tuple, - TypeVar, Union, overload, ) +from utils import _FETCHABLE + from . import abc, utils from .asset import Asset from .automod import AutoModAction, AutoModRule, AutoModTriggerMetadata @@ -864,11 +865,6 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) - _FETCHABLE = TypeVar( - "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | GuildEmoji", - ) - async def get_or_fetch( self: Guild, object_type: type[_FETCHABLE], From 5421a3f1365c0d320259dd3afbc5888fb58a324e Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 23:00:23 +0300 Subject: [PATCH 052/111] fix: update import statement for _FETCHABLE from utils module --- discord/guild.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 99090bf35c..4d64eb419d 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -40,8 +40,6 @@ overload, ) -from utils import _FETCHABLE - from . import abc, utils from .asset import Asset from .automod import AutoModAction, AutoModRule, AutoModTriggerMetadata @@ -87,6 +85,7 @@ from .sticker import GuildSticker from .threads import Thread, ThreadMember from .user import User +from .utils import _FETCHABLE from .welcome_screen import WelcomeScreen, WelcomeScreenChannel from .widget import Widget From 50318c25c4616800edda71db36e6c6889f0f8a06 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 9 May 2025 23:48:27 +0300 Subject: [PATCH 053/111] refactor: streamline get_or_fetch logic with a mapping for object types --- discord/utils.py | 54 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index ae49a8e1ba..375863160d 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -692,29 +692,51 @@ async def get_or_fetch( raise InvalidArgument( f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" ) + if isinstance(obj, Guild) and object_type is User: raise InvalidArgument( "Guild cannot get_or_fetch discord.User. Use Client instead." ) - elif isinstance(obj, Client) and object_type is Member: + if isinstance(obj, Client) and object_type is Member: raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") - getter = getattr(obj, f"get_{attr}", None) - if getter: - result = getter(object_id) - if result is not None: - return result + getter_fetcher_map = { + Member: ( + lambda obj, oid: obj.get_member(oid), + lambda obj, oid: obj.fetch_member(oid), + ), + User: ( + lambda obj, oid: obj.get_user(oid), + lambda obj, oid: obj.fetch_user(oid), + ), + Guild: ( + lambda obj, oid: obj.get_guild(oid), + lambda obj, oid: obj.fetch_guild(oid), + ), + emoji._EmojiTag: ( + lambda obj, oid: obj.get_emoji(oid), + lambda obj, oid: obj.fetch_emoji(oid), + ), + abc.GuildChannel: ( + lambda obj, oid: obj.get_channel(oid), + lambda obj, oid: obj.fetch_channel(oid), + ), + } + try: + getter, fetcher = getter_fetcher_map[object_type] + except KeyError: + raise InvalidArgument(f"Unsupported object type: {object_type.__name__}") + + result = getter(obj, object_id) + if result is not None: + return result - fetcher = getattr(obj, f"fetch_{attr}", None) or getattr( - obj, f"_fetch_{attr}", None - ) - if fetcher: - try: - return await fetcher(object_id) - except (HTTPException, ValueError): - if default is not MISSING: - return default - raise + try: + return await fetcher(obj, object_id) + except (HTTPException, ValueError): + if default is not None: + return default + raise def _unique(iterable: Iterable[T]) -> list[T]: From 541e668ca3909187dedc8260c02714f386e119cd Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 10 May 2025 00:00:43 +0300 Subject: [PATCH 054/111] fix: add validation for Guild type in get_or_fetch function --- discord/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 375863160d..c485594602 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -700,6 +700,9 @@ async def get_or_fetch( if isinstance(obj, Client) and object_type is Member: raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") + if isinstance(obj, Guild) and object_type is Guild: + raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") + getter_fetcher_map = { Member: ( lambda obj, oid: obj.get_member(oid), @@ -723,7 +726,10 @@ async def get_or_fetch( ), } try: - getter, fetcher = getter_fetcher_map[object_type] + base_type = next( + base for base in getter_fetcher_map if issubclass(object_type, base_type) + ) + getter, fetcher = getter_fetcher_map[base_type] except KeyError: raise InvalidArgument(f"Unsupported object type: {object_type.__name__}") From b20cffbc6eee836b0e45303c841eeb0ac9cd14fb Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 10 May 2025 00:03:25 +0300 Subject: [PATCH 055/111] fix: correct base type resolution in get_or_fetch function --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index c485594602..7fe0047179 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -727,7 +727,7 @@ async def get_or_fetch( } try: base_type = next( - base for base in getter_fetcher_map if issubclass(object_type, base_type) + base for base in getter_fetcher_map if issubclass(object_type, base) ) getter, fetcher = getter_fetcher_map[base_type] except KeyError: From 3e94a4fff96b7ab32fdb0cb6e0ad43928bf4c596 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 14 May 2025 20:18:46 +0300 Subject: [PATCH 056/111] fix: add Role type support in get_or_fetch function --- discord/guild.py | 4 ++-- discord/utils.py | 33 +++++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 4d64eb419d..8d3584bdec 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -875,7 +875,7 @@ async def get_or_fetch( Parameters ---------- - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`] Type of object to fetch or get. object_id: :class:`int` @@ -887,7 +887,7 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Member`, :class:`GuildEmoji`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`]] The object of type that was specified or ``None`` if not found. Raises diff --git a/discord/utils.py b/discord/utils.py index 7fe0047179..4cddb7ee5e 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -75,6 +75,7 @@ Member, User, Guild, + Role, GuildEmoji, AppEmoji, ) @@ -591,7 +592,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | GuildEmoji | AppEmoji", + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | Role | GuildEmoji | AppEmoji", ) @@ -610,7 +611,7 @@ async def get_or_fetch( ---------- obj : Guild | Client The object to operate on. - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`] + object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`] Type of object to fetch or get. object_id: :class:`int` @@ -622,7 +623,7 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`]] + Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`]] The object of type that was specified or ``None`` if not found. Raises @@ -634,15 +635,7 @@ async def get_or_fetch( :exc:`Forbidden` You do not have permission to fetch the object """ - from discord import ( - AppEmoji, - Client, - Guild, - Member, - User, - abc, - emoji, - ) + from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji string_to_type = { "channel": abc.GuildChannel, @@ -651,6 +644,7 @@ async def get_or_fetch( "guild": Guild, "emoji": emoji._EmojiTag, "appemoji": AppEmoji, + "role": Role, } if attr is not MISSING or id is not MISSING or isinstance(object_type, str): @@ -686,6 +680,8 @@ async def get_or_fetch( attr = object_type.__name__.lower() elif issubclass(object_type, emoji._EmojiTag): attr = "emoji" + elif issubclass(object_type, Role): + attr = "role" elif issubclass(object_type, abc.GuildChannel): attr = "channel" else: @@ -697,17 +693,22 @@ async def get_or_fetch( raise InvalidArgument( "Guild cannot get_or_fetch discord.User. Use Client instead." ) - if isinstance(obj, Client) and object_type is Member: - raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") - - if isinstance(obj, Guild) and object_type is Guild: + elif isinstance(obj, Client) and object_type is Member: raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") + elif isinstance(obj, Client) and object_type is Role: + raise InvalidArgument("Client cannot get_or_fetch Role. Use Guild instead.") + elif isinstance(obj, Guild) and object_type is Guild: + raise InvalidArgument("Guild cannot get_or_fetch Guild. Use Client instead.") getter_fetcher_map = { Member: ( lambda obj, oid: obj.get_member(oid), lambda obj, oid: obj.fetch_member(oid), ), + Role: ( + lambda obj, oid: obj.get_member(oid), + lambda obj, oid: obj.fetch_member(oid), + ), User: ( lambda obj, oid: obj.get_user(oid), lambda obj, oid: obj.fetch_user(oid), From 7384d7c13ddf62cb48e1923ffbc2ca49d92342b5 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 14 May 2025 21:17:37 +0300 Subject: [PATCH 057/111] fix: update role retrieval methods in get_or_fetch function --- discord/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 4cddb7ee5e..4e750c8400 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -706,8 +706,8 @@ async def get_or_fetch( lambda obj, oid: obj.fetch_member(oid), ), Role: ( - lambda obj, oid: obj.get_member(oid), - lambda obj, oid: obj.fetch_member(oid), + lambda obj, oid: obj.get_role(oid), + lambda obj, oid: obj._fetch_role(oid), ), User: ( lambda obj, oid: obj.get_user(oid), From 0ffaa4b3e1c543938646faab3d8305e215fb99cd Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Thu, 15 May 2025 21:44:32 +0300 Subject: [PATCH 058/111] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e8ada1094..35b98794eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) -- Added `Guild.get_or_fetch()` shortcut method and better get_or_fetch +- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods, and deprecated the old `get_or_fetch` behavior. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ### Fixed From 92d2987bbaba4668cf79d3cf58c6d6e2b0f205f6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 18:44:58 +0000 Subject: [PATCH 059/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b98794eb..3921e7addf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) -- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods, and deprecated the old `get_or_fetch` behavior. +- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods, and + deprecated the old `get_or_fetch` behavior. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ### Fixed From b8c7f7f91714d25459757c1bb7470c26bcb86412 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 17 May 2025 13:28:47 +0300 Subject: [PATCH 060/111] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3921e7addf..ca9565517d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,8 +53,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2714](https://github.com/Pycord-Development/pycord/pull/2714)) - Added the ability to pass a `datetime.time` object to `format_dt` ([#2747](https://github.com/Pycord-Development/pycord/pull/2747)) -- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods, and - deprecated the old `get_or_fetch` behavior. +- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ### Fixed @@ -138,6 +137,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) +- Deprecated the old `get_or_fetch(attr, id)` in favor of `get_or_fetch(object_type, object_id`. + ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ## [2.6.1] - 2024-09-15 From 53dc93ca7626eec0aa646fc108fa4b2c957c13db Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 10:29:14 +0000 Subject: [PATCH 061/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9565517d..c7d3dd2e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,7 +137,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated the old `get_or_fetch(attr, id)` in favor of `get_or_fetch(object_type, object_id`. +- Deprecated the old `get_or_fetch(attr, id)` in favor of + `get_or_fetch(object_type, object_id`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ## [2.6.1] - 2024-09-15 From 6d89376b7090b91498e602ac42dca3324d9ec23a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 17 May 2025 13:30:01 +0300 Subject: [PATCH 062/111] Update CHANGELOG.md Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d3dd2e89..cbf0a762ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,8 +137,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated the old `get_or_fetch(attr, id)` in favor of - `get_or_fetch(object_type, object_id`. +- Deprecated `utils.get_or_fetch(attr, id)` in favor of `utils.get_or_fetch(object_type, object_id`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ## [2.6.1] - 2024-09-15 From e2e8b3d35ea9ad7779e98cee01feaeeba5b47d76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 10:30:27 +0000 Subject: [PATCH 063/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbf0a762ee..392488d129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,7 +137,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated `utils.get_or_fetch(attr, id)` in favor of `utils.get_or_fetch(object_type, object_id`. +- Deprecated `utils.get_or_fetch(attr, id)` in favor of + `utils.get_or_fetch(object_type, object_id`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ## [2.6.1] - 2024-09-15 From 2abfba258a05b26b65f3cabd8809952b64ec4320 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 18 May 2025 10:18:40 +0300 Subject: [PATCH 064/111] Update CHANGELOG.md Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392488d129..67f1b10b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,7 +138,7 @@ These changes are available on the `master` branch, but have not yet been releas - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) - Deprecated `utils.get_or_fetch(attr, id)` in favor of - `utils.get_or_fetch(object_type, object_id`. + `utils.get_or_fetch(object_type, object_id)`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ## [2.6.1] - 2024-09-15 From c60fbab6e1b1a00a911a53ced25b3738776f8152 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:46:37 +0300 Subject: [PATCH 065/111] feat: update get_or_fetch method to accept Optional[int] for object_id --- discord/client.py | 12 ++++++++++-- discord/guild.py | 2 +- discord/utils.py | 5 ++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/discord/client.py b/discord/client.py index c91b5e3f28..d8e3776340 100644 --- a/discord/client.py +++ b/discord/client.py @@ -31,7 +31,15 @@ import sys import traceback from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Coroutine, + Generator, + Sequence, + TypeVar, +) import aiohttp @@ -1147,7 +1155,7 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in async def get_or_fetch( self: Client, object_type: type[_FETCHABLE], - object_id: int, + object_id: int | None, default: Any = MISSING, ) -> _FETCHABLE | None: """ diff --git a/discord/guild.py b/discord/guild.py index 8d3584bdec..e5a1879a8e 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -867,7 +867,7 @@ def get_member(self, user_id: int, /) -> Member | None: async def get_or_fetch( self: Guild, object_type: type[_FETCHABLE], - object_id: int, + object_id: int | None, default: Any = MISSING, ) -> _FETCHABLE | None: """ diff --git a/discord/utils.py b/discord/utils.py index 4e750c8400..34923760b7 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -599,7 +599,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, - object_id: int = MISSING, + object_id: int | None = MISSING, default: Any = MISSING, attr: str = MISSING, id: int = MISSING, @@ -637,6 +637,9 @@ async def get_or_fetch( """ from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji + if object_id is None: + return None + string_to_type = { "channel": abc.GuildChannel, "member": Member, From 4a5eca4a2c77e31c97f2960e385bac47bb1c0e9a Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:46:45 +0300 Subject: [PATCH 066/111] fix: "MISSING" not being exported --- discord/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/utils.py b/discord/utils.py index 7df6e92a0a..f5b2dbf07b 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -114,6 +114,7 @@ "generate_snowflake", "basic_autocomplete", "filter_params", + "MISSING", ) DISCORD_EPOCH = 1420070400000 From f81a3389c09cd985bd5341d0446cc074cb799c74 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 13 Jul 2025 12:27:11 +0200 Subject: [PATCH 067/111] add some more comment --- discord/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 7ec7165694..86759c4041 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -598,7 +598,9 @@ async def get_or_fetch( default: Any = MISSING, attr: str = MISSING, id: int = MISSING, -) -> _FETCHABLE | None: # TODO: Remove in 3.0 the arguments attr and id +) -> ( + _FETCHABLE | None +): # TODO: Remove in 3.0 the arguments attr and id + remove the = MISSING for both object_type and object_id """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. @@ -644,12 +646,13 @@ async def get_or_fetch( "appemoji": AppEmoji, "role": Role, } - + # backward support for attr and id, this whole if block should be removed in v3 if attr is not MISSING or id is not MISSING or isinstance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr='type', id=...)", instead="get_or_fetch(obj, object_type=Type, object_id=...)", since="2.7", + removed="3.0", ) deprecated_attr = attr if attr is not MISSING else object_type From cc273c214297ef7e23daed6943627d555486358b Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 13 Jul 2025 12:28:48 +0200 Subject: [PATCH 068/111] Update utils.py --- discord/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 86759c4041..368f984bf0 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -591,6 +591,8 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: ) +# TODO: In version 3.0, remove the 'attr' and 'id' arguments. +# Also, eliminate the default 'MISSING' value for both 'object_type' and 'object_id'. async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, @@ -598,9 +600,7 @@ async def get_or_fetch( default: Any = MISSING, attr: str = MISSING, id: int = MISSING, -) -> ( - _FETCHABLE | None -): # TODO: Remove in 3.0 the arguments attr and id + remove the = MISSING for both object_type and object_id +) -> _FETCHABLE | None: """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. @@ -646,7 +646,8 @@ async def get_or_fetch( "appemoji": AppEmoji, "role": Role, } - # backward support for attr and id, this whole if block should be removed in v3 + # Temporary backward compatibility for 'attr' and 'id'. + # This entire if block should be removed in version 3.0. if attr is not MISSING or id is not MISSING or isinstance(object_type, str): warn_deprecated( name="get_or_fetch(obj, attr='type', id=...)", From 2837e94750a9218ecf7c04204f2653fce59b0578 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 20:07:14 +0000 Subject: [PATCH 069/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index 119e9cdefd..ffd9bde91c 100644 --- a/discord/client.py +++ b/discord/client.py @@ -83,11 +83,11 @@ TextChannel, VoiceChannel, ) + from .interaction import Interaction from .member import Member from .message import Message from .poll import Poll from .threads import Thread, ThreadMember - from .interaction import Interaction from .ui.item import Item from .voice_client import VoiceProtocol From b70c6663c35d4ef88560271fea9650b4982fbc2f Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:34:14 +0200 Subject: [PATCH 070/111] Update discord/utils.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 368f984bf0..2f60c5513b 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -614,7 +614,7 @@ async def get_or_fetch( object_id: :class:`int` ID of object to get. - default : Any, optional + default : Any | None A default to return instead of raising if fetch fails. Returns From 198556d662bbc019e4a1dec6111a944178be3c7d Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:37:33 +0200 Subject: [PATCH 071/111] Update discord/utils.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 2f60c5513b..8b4d8a8c9d 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -615,7 +615,7 @@ async def get_or_fetch( ID of object to get. default : Any | None - A default to return instead of raising if fetch fails. + The value to return instead of raising if fetching fails. Returns ------- From 91b3d683166cf88480a7860cf8c32fb3742dcd97 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:36:46 +0200 Subject: [PATCH 072/111] comment --- discord/client.py | 58 +++++++++++++++++++++++++++++---------- discord/guild.py | 61 ++++++++++++++++++++++++++++++----------- discord/utils.py | 70 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 148 insertions(+), 41 deletions(-) diff --git a/discord/client.py b/discord/client.py index ffd9bde91c..f4c10c7726 100644 --- a/discord/client.py +++ b/discord/client.py @@ -39,6 +39,7 @@ Generator, Sequence, TypeVar, + overload, ) import aiohttp @@ -68,7 +69,7 @@ from .threads import Thread from .ui.view import View from .user import ClientUser, User -from .utils import _FETCHABLE, MISSING +from .utils import _FETCHABLE, MISSING, _D from .voice_client import VoiceClient from .webhook import Webhook from .widget import Widget @@ -247,9 +248,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( - {} - ) + self._listeners: dict[ + str, list[tuple[asyncio.Future, Callable[..., bool]]] + ] = {} self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") @@ -1186,37 +1187,64 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in return await self.get_or_fetch(object_type=User, object_id=id, default=None) + @overload + async def get_or_fetch( + self: Client, + object_type: type[_FETCHABLE], + object_id: None, + default: _D = ..., + ) -> None | _D: ... + + @overload async def get_or_fetch( self: Client, object_type: type[_FETCHABLE], + object_id: int, + default: _D, + ) -> _FETCHABLE | _D: ... + + @overload + async def get_or_fetch( + self: "Client", + object_type: type[_FETCHABLE], + object_id: int, + ) -> _FETCHABLE: ... + + async def get_or_fetch( + self: "Client", + object_type: type[_FETCHABLE], object_id: int | None, - default: Any = MISSING, - ) -> _FETCHABLE | None: + default: _D = MISSING, + ) -> _FETCHABLE | _D | None: """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. Parameters ---------- - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`GuildEmoji`, :class:`AppEmoji`] + object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji Type of object to fetch or get. - object_id: :class:`int` - ID of object to get. - default : Any, optional + object_id: int | None + ID of object to get. If None, returns default if provided, else None. + default: Any, optional A default to return instead of raising if fetch fails. Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`GuildEmoji`, :class:`AppEmoji`]] - The object of type that was specified or ``None`` if not found. + VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji | None + The object of the specified type, or default if provided and not found, or None if not found and no default is provided. Raises ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. :exc:`NotFound` - Invalid ID for the object + Invalid ID for the object. :exc:`HTTPException` - An error occurred fetching the object + An error occurred fetching the object. :exc:`Forbidden` - You do not have permission to fetch the object + You do not have permission to fetch the object. """ return await utils.get_or_fetch( obj=self, object_type=object_type, object_id=object_id, default=default diff --git a/discord/guild.py b/discord/guild.py index f8a367b690..0fb9af5485 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -85,7 +85,7 @@ from .sticker import GuildSticker from .threads import Thread, ThreadMember from .user import User -from .utils import _FETCHABLE +from .utils import _FETCHABLE, _D from .welcome_screen import WelcomeScreen, WelcomeScreenChannel from .widget import Widget @@ -864,43 +864,72 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) + @overload + async def get_or_fetch( + self: Guild, + object_type: type[_FETCHABLE], + object_id: None, + default: _D = ..., + ) -> None | _D: ... + @overload + async def get_or_fetch( + self: Guild, + object_type: type[_FETCHABLE], + object_id: int, + default: _D, + ) -> _FETCHABLE | _D: ... + @overload + async def get_or_fetch( + self: Guild, + object_type: type[_FETCHABLE], + object_id: int, + ) -> _FETCHABLE: ... + async def get_or_fetch( self: Guild, object_type: type[_FETCHABLE], object_id: int | None, - default: Any = MISSING, - ) -> _FETCHABLE | None: + default: _D = MISSING, + ) -> _FETCHABLE | _D | None: """ - Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. + Shortcut method to get data from this guild either by returning the cached version, + or if it does not exist, attempting to fetch it from the API. Parameters ---------- - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`] + object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji Type of object to fetch or get. - object_id: :class:`int` - ID of object to get. + object_id: :class:`int` | None + ID of the object to get. If ``None``, returns ``default`` if provided, otherwise ``None``. - default : Any, optional - A default to return instead of raising if fetch fails. + default : Any | None + The value to return instead of raising if fetching fails or if ``object_id`` is ``None``. Returns ------- - - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`]] - The object of type that was specified or ``None`` if not found. + VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji | None + The object if found, or ``default`` if provided when not found. + Returns ``None`` only if ``object_id`` is ``None`` and no ``default`` is given. Raises ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. :exc:`NotFound` - Invalid ID for the object + Invalid ID for the object. :exc:`HTTPException` - An error occurred fetching the object + An error occurred fetching the object. :exc:`Forbidden` - You do not have permission to fetch the object + You do not have permission to fetch the object. """ return await utils.get_or_fetch( - obj=self, object_type=object_type, object_id=object_id, default=default + obj=self, + object_type=object_type, + object_id=object_id, + default=default, ) @property diff --git a/discord/utils.py b/discord/utils.py index 8b4d8a8c9d..d3bed4e368 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -587,20 +587,65 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | Role | GuildEmoji | AppEmoji", + bound=VoiceChannel + | TextChannel + | ForumChannel + | StageChannel + | CategoryChannel + | Thread + | Member + | User + | Guild + | Role + | GuildEmoji + | AppEmoji, ) +_D = TypeVar("_D") # TODO: In version 3.0, remove the 'attr' and 'id' arguments. # Also, eliminate the default 'MISSING' value for both 'object_type' and 'object_id'. +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: None, + default: _D = ..., + attr: str = ..., + id: int = ..., +) -> None | _D: ... + + +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: int, + default: _D, + attr: str = ..., + id: int = ..., +) -> _FETCHABLE | _D: ... + + +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: int, + *, + attr: str = ..., + id: int = ..., +) -> _FETCHABLE: ... + + async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, object_id: int | None = MISSING, - default: Any = MISSING, + default: _D = MISSING, attr: str = MISSING, id: int = MISSING, -) -> _FETCHABLE | None: +) -> _FETCHABLE | _D | None: """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. @@ -608,7 +653,7 @@ async def get_or_fetch( ---------- obj : Guild | Client The object to operate on. - object_type: Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`] + object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji Type of object to fetch or get. object_id: :class:`int` @@ -620,22 +665,27 @@ async def get_or_fetch( Returns ------- - Optional[Union[:class:`VoiceChannel`, :class:`TextChannel`, :class:`ForumChannel`, :class:`StageChannel`, :class:`CategoryChannel`, :class:`Thread`, :class:`User`, :class:`Guild`, :class:`Role`, :class:`Member`, :class:`GuildEmoji`, :class:`AppEmoji`]] - The object of type that was specified or ``None`` if not found. + VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji | None + The object if found, or `default` if provided when not found. + Returns `None` only if `object_id` is None and no `default` is given. Raises ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. :exc:`NotFound` - Invalid ID for the object + Invalid ID for the object. :exc:`HTTPException` - An error occurred fetching the object + An error occurred fetching the object. :exc:`Forbidden` - You do not have permission to fetch the object + You do not have permission to fetch the object. """ from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji if object_id is None: - return None + return default if default is not MISSING else None string_to_type = { "channel": abc.GuildChannel, From cb38bd7eaa6746efb22764d823d66d2eb6df2e09 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 2 Aug 2025 13:37:16 +0000 Subject: [PATCH 073/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 12 ++++++------ discord/guild.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/discord/client.py b/discord/client.py index f4c10c7726..69ff68d2ee 100644 --- a/discord/client.py +++ b/discord/client.py @@ -69,7 +69,7 @@ from .threads import Thread from .ui.view import View from .user import ClientUser, User -from .utils import _FETCHABLE, MISSING, _D +from .utils import _D, _FETCHABLE, MISSING from .voice_client import VoiceClient from .webhook import Webhook from .widget import Widget @@ -248,9 +248,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") @@ -1205,13 +1205,13 @@ async def get_or_fetch( @overload async def get_or_fetch( - self: "Client", + self: Client, object_type: type[_FETCHABLE], object_id: int, ) -> _FETCHABLE: ... async def get_or_fetch( - self: "Client", + self: Client, object_type: type[_FETCHABLE], object_id: int | None, default: _D = MISSING, diff --git a/discord/guild.py b/discord/guild.py index 0fb9af5485..ffa260c5c7 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -85,7 +85,7 @@ from .sticker import GuildSticker from .threads import Thread, ThreadMember from .user import User -from .utils import _FETCHABLE, _D +from .utils import _D, _FETCHABLE from .welcome_screen import WelcomeScreen, WelcomeScreenChannel from .widget import Widget From f13e86cc440700d34ef02ec6870dc8691a18c2aa Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:41:03 +0200 Subject: [PATCH 074/111] fix docs --- discord/utils.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index d3bed4e368..b2c9509061 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -587,18 +587,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: _FETCHABLE = TypeVar( "_FETCHABLE", - bound=VoiceChannel - | TextChannel - | ForumChannel - | StageChannel - | CategoryChannel - | Thread - | Member - | User - | Guild - | Role - | GuildEmoji - | AppEmoji, + bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | Role | GuildEmoji | AppEmoji", ) _D = TypeVar("_D") From da5948589d6063d694f23ece4e8a5da26151198b Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:56:51 +0200 Subject: [PATCH 075/111] Update discord/client.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index 69ff68d2ee..c6b78a4f7a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1225,7 +1225,7 @@ async def get_or_fetch( Type of object to fetch or get. object_id: int | None ID of object to get. If None, returns default if provided, else None. - default: Any, optional + default: Any | None A default to return instead of raising if fetch fails. Returns From fa0efba0a6c0c26dab47451077d4a124d0c02828 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:58:41 +0200 Subject: [PATCH 076/111] usage of literal None --- discord/client.py | 9 +++++---- discord/guild.py | 3 ++- discord/utils.py | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/discord/client.py b/discord/client.py index 69ff68d2ee..4631c44df0 100644 --- a/discord/client.py +++ b/discord/client.py @@ -40,6 +40,7 @@ Sequence, TypeVar, overload, + Literal, ) import aiohttp @@ -248,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( - {} - ) + self._listeners: dict[ + str, list[tuple[asyncio.Future, Callable[..., bool]]] + ] = {} self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") @@ -1191,7 +1192,7 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in async def get_or_fetch( self: Client, object_type: type[_FETCHABLE], - object_id: None, + object_id: Literal[None], default: _D = ..., ) -> None | _D: ... diff --git a/discord/guild.py b/discord/guild.py index ffa260c5c7..49f34c5aa6 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -38,6 +38,7 @@ Tuple, Union, overload, + Literal, ) from . import abc, utils @@ -868,7 +869,7 @@ def get_member(self, user_id: int, /) -> Member | None: async def get_or_fetch( self: Guild, object_type: type[_FETCHABLE], - object_id: None, + object_id: Literal[None], default: _D = ..., ) -> None | _D: ... @overload diff --git a/discord/utils.py b/discord/utils.py index b2c9509061..d2cbb67158 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -598,7 +598,7 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE], - object_id: None, + object_id: Literal[None], default: _D = ..., attr: str = ..., id: int = ..., @@ -782,7 +782,7 @@ async def get_or_fetch( try: return await fetcher(obj, object_id) except (HTTPException, ValueError): - if default is not None: + if default is not MISSING: return default raise From 7d125034b2a6c1e636aac4acafd577fd6e444cc3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 2 Aug 2025 13:59:21 +0000 Subject: [PATCH 077/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 8 ++++---- discord/guild.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/discord/client.py b/discord/client.py index f2cb8f4c93..8958e5c472 100644 --- a/discord/client.py +++ b/discord/client.py @@ -37,10 +37,10 @@ Callable, Coroutine, Generator, + Literal, Sequence, TypeVar, overload, - Literal, ) import aiohttp @@ -249,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") diff --git a/discord/guild.py b/discord/guild.py index 49f34c5aa6..f8cbfe2904 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -32,13 +32,13 @@ Any, ClassVar, List, + Literal, NamedTuple, Optional, Sequence, Tuple, Union, overload, - Literal, ) from . import abc, utils From d0c524c9daf08c51eda1e540d9a1f3d79ef130e4 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 3 Aug 2025 10:16:42 +0200 Subject: [PATCH 078/111] return None when fetching fails --- discord/client.py | 45 ++++++---------------------------------- discord/guild.py | 38 ++------------------------------- discord/utils.py | 53 +++-------------------------------------------- 3 files changed, 11 insertions(+), 125 deletions(-) diff --git a/discord/client.py b/discord/client.py index f2cb8f4c93..164293d704 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1188,34 +1188,11 @@ async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in return await self.get_or_fetch(object_type=User, object_id=id, default=None) - @overload - async def get_or_fetch( - self: Client, - object_type: type[_FETCHABLE], - object_id: Literal[None], - default: _D = ..., - ) -> None | _D: ... - - @overload - async def get_or_fetch( - self: Client, - object_type: type[_FETCHABLE], - object_id: int, - default: _D, - ) -> _FETCHABLE | _D: ... - - @overload - async def get_or_fetch( - self: Client, - object_type: type[_FETCHABLE], - object_id: int, - ) -> _FETCHABLE: ... - async def get_or_fetch( self: Client, object_type: type[_FETCHABLE], object_id: int | None, - default: _D = MISSING, + default: _D = None, ) -> _FETCHABLE | _D | None: """ Shortcut method to get data from an object either by returning the cached version, or if it does not exist, attempting to fetch it from the API. @@ -1232,23 +1209,13 @@ async def get_or_fetch( Returns ------- VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji | None - The object of the specified type, or default if provided and not found, or None if not found and no default is provided. - - Raises - ------ - :exc:`TypeError` - Raised when required parameters are missing or invalid types are provided. - :exc:`InvalidArgument` - Raised when an unsupported or incompatible object type is used. - :exc:`NotFound` - Invalid ID for the object. - :exc:`HTTPException` - An error occurred fetching the object. - :exc:`Forbidden` - You do not have permission to fetch the object. + The object if found, or `default` if provided when not found. """ return await utils.get_or_fetch( - obj=self, object_type=object_type, object_id=object_id, default=default + obj=self, + object_type=object_type, + object_id=object_id, + default=default, ) # listeners/waiters diff --git a/discord/guild.py b/discord/guild.py index 49f34c5aa6..7c5a19d4a9 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -865,32 +865,11 @@ def get_member(self, user_id: int, /) -> Member | None: """ return self._members.get(user_id) - @overload - async def get_or_fetch( - self: Guild, - object_type: type[_FETCHABLE], - object_id: Literal[None], - default: _D = ..., - ) -> None | _D: ... - @overload - async def get_or_fetch( - self: Guild, - object_type: type[_FETCHABLE], - object_id: int, - default: _D, - ) -> _FETCHABLE | _D: ... - @overload - async def get_or_fetch( - self: Guild, - object_type: type[_FETCHABLE], - object_id: int, - ) -> _FETCHABLE: ... - async def get_or_fetch( self: Guild, object_type: type[_FETCHABLE], object_id: int | None, - default: _D = MISSING, + default: _D = None, ) -> _FETCHABLE | _D | None: """ Shortcut method to get data from this guild either by returning the cached version, @@ -910,21 +889,8 @@ async def get_or_fetch( Returns ------- VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji | None - The object if found, or ``default`` if provided when not found. - Returns ``None`` only if ``object_id`` is ``None`` and no ``default`` is given. + The object if found, or `default` if provided when not found. - Raises - ------ - :exc:`TypeError` - Raised when required parameters are missing or invalid types are provided. - :exc:`InvalidArgument` - Raised when an unsupported or incompatible object type is used. - :exc:`NotFound` - Invalid ID for the object. - :exc:`HTTPException` - An error occurred fetching the object. - :exc:`Forbidden` - You do not have permission to fetch the object. """ return await utils.get_or_fetch( obj=self, diff --git a/discord/utils.py b/discord/utils.py index d2cbb67158..623aeedba5 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -594,44 +594,13 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: # TODO: In version 3.0, remove the 'attr' and 'id' arguments. # Also, eliminate the default 'MISSING' value for both 'object_type' and 'object_id'. -@overload -async def get_or_fetch( - obj: Guild | Client, - object_type: type[_FETCHABLE], - object_id: Literal[None], - default: _D = ..., - attr: str = ..., - id: int = ..., -) -> None | _D: ... - - -@overload -async def get_or_fetch( - obj: Guild | Client, - object_type: type[_FETCHABLE], - object_id: int, - default: _D, - attr: str = ..., - id: int = ..., -) -> _FETCHABLE | _D: ... - - -@overload -async def get_or_fetch( - obj: Guild | Client, - object_type: type[_FETCHABLE], - object_id: int, - *, - attr: str = ..., - id: int = ..., -) -> _FETCHABLE: ... async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, object_id: int | None = MISSING, - default: _D = MISSING, + default: _D = None, attr: str = MISSING, id: int = MISSING, ) -> _FETCHABLE | _D | None: @@ -645,7 +614,7 @@ async def get_or_fetch( object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji Type of object to fetch or get. - object_id: :class:`int` + object_id: int | None ID of object to get. default : Any | None @@ -656,20 +625,6 @@ async def get_or_fetch( VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji | None The object if found, or `default` if provided when not found. - Returns `None` only if `object_id` is None and no `default` is given. - - Raises - ------ - :exc:`TypeError` - Raised when required parameters are missing or invalid types are provided. - :exc:`InvalidArgument` - Raised when an unsupported or incompatible object type is used. - :exc:`NotFound` - Invalid ID for the object. - :exc:`HTTPException` - An error occurred fetching the object. - :exc:`Forbidden` - You do not have permission to fetch the object. """ from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji @@ -782,9 +737,7 @@ async def get_or_fetch( try: return await fetcher(obj, object_id) except (HTTPException, ValueError): - if default is not MISSING: - return default - raise + return default def _unique(iterable: Iterable[T]) -> list[T]: From 6c6e33a2801755aee4f5c6511e1a032973b8c5a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 3 Aug 2025 08:17:14 +0000 Subject: [PATCH 079/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 2 -- discord/guild.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/discord/client.py b/discord/client.py index dac2a54df1..372ad7d666 100644 --- a/discord/client.py +++ b/discord/client.py @@ -37,10 +37,8 @@ Callable, Coroutine, Generator, - Literal, Sequence, TypeVar, - overload, ) import aiohttp diff --git a/discord/guild.py b/discord/guild.py index 4313cfaa82..36bc2d598d 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -32,7 +32,6 @@ Any, ClassVar, List, - Literal, NamedTuple, Optional, Sequence, @@ -890,7 +889,6 @@ async def get_or_fetch( ------- VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji | None The object if found, or `default` if provided when not found. - """ return await utils.get_or_fetch( obj=self, From ea555a1877b71968c8ed7a3aba9a6a4dd219cde2 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 3 Aug 2025 11:21:23 +0200 Subject: [PATCH 080/111] back to non breaking --- discord/client.py | 22 ++++++++++----- discord/guild.py | 23 +++++++++++----- discord/utils.py | 68 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 84 insertions(+), 29 deletions(-) diff --git a/discord/client.py b/discord/client.py index 164293d704..273395d383 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1210,13 +1210,23 @@ async def get_or_fetch( ------- VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji | None The object if found, or `default` if provided when not found. + + Raises + ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. """ - return await utils.get_or_fetch( - obj=self, - object_type=object_type, - object_id=object_id, - default=default, - ) + try: + return await utils.get_or_fetch( + obj=self, + object_type=object_type, + object_id=object_id, + default=default, + ) + except (HTTPException, ValueError): + return default # listeners/waiters diff --git a/discord/guild.py b/discord/guild.py index 7c5a19d4a9..f39b54c84a 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -64,7 +64,7 @@ VoiceRegion, try_enum, ) -from .errors import ClientException, InvalidArgument, InvalidData +from .errors import ClientException, InvalidArgument, InvalidData, HTTPException from .file import File from .flags import SystemChannelFlags from .integrations import Integration, _integration_factory @@ -891,13 +891,22 @@ async def get_or_fetch( VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji | None The object if found, or `default` if provided when not found. + Raises + ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. """ - return await utils.get_or_fetch( - obj=self, - object_type=object_type, - object_id=object_id, - default=default, - ) + try: + return await utils.get_or_fetch( + obj=self, + object_type=object_type, + object_id=object_id, + default=default, + ) + except (HTTPException, ValueError): + return default @property def premium_subscribers(self) -> list[Member]: diff --git a/discord/utils.py b/discord/utils.py index 623aeedba5..4ee9563add 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -594,13 +594,44 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: # TODO: In version 3.0, remove the 'attr' and 'id' arguments. # Also, eliminate the default 'MISSING' value for both 'object_type' and 'object_id'. +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: Literal[None], + default: _D = ..., + attr: str = ..., + id: int = ..., +) -> None | _D: ... + + +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: int, + default: _D, + attr: str = ..., + id: int = ..., +) -> _FETCHABLE | _D: ... + + +@overload +async def get_or_fetch( + obj: Guild | Client, + object_type: type[_FETCHABLE], + object_id: int, + *, + attr: str = ..., + id: int = ..., +) -> _FETCHABLE: ... async def get_or_fetch( obj: Guild | Client, object_type: type[_FETCHABLE] = MISSING, object_id: int | None = MISSING, - default: _D = None, + default: _D = MISSING, attr: str = MISSING, id: int = MISSING, ) -> _FETCHABLE | _D | None: @@ -625,6 +656,20 @@ async def get_or_fetch( VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji | None The object if found, or `default` if provided when not found. + Returns `None` only if `object_id` is None and no `default` is given. + + Raises + ------ + :exc:`TypeError` + Raised when required parameters are missing or invalid types are provided. + :exc:`InvalidArgument` + Raised when an unsupported or incompatible object type is used. + :exc:`NotFound` + Invalid ID for the object. + :exc:`HTTPException` + An error occurred fetching the object. + :exc:`Forbidden` + You do not have permission to fetch the object. """ from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji @@ -672,19 +717,6 @@ async def get_or_fetch( if object_type is MISSING or object_id is MISSING: raise TypeError("required parameters: `object_type` and `object_id`.") - if issubclass(object_type, (Member, User, Guild)): - attr = object_type.__name__.lower() - elif issubclass(object_type, emoji._EmojiTag): - attr = "emoji" - elif issubclass(object_type, Role): - attr = "role" - elif issubclass(object_type, abc.GuildChannel): - attr = "channel" - else: - raise InvalidArgument( - f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" - ) - if isinstance(obj, Guild) and object_type is User: raise InvalidArgument( "Guild cannot get_or_fetch discord.User. Use Client instead." @@ -728,7 +760,9 @@ async def get_or_fetch( ) getter, fetcher = getter_fetcher_map[base_type] except KeyError: - raise InvalidArgument(f"Unsupported object type: {object_type.__name__}") + raise InvalidArgument( + f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + ) result = getter(obj, object_id) if result is not None: @@ -737,7 +771,9 @@ async def get_or_fetch( try: return await fetcher(obj, object_id) except (HTTPException, ValueError): - return default + if default is not MISSING: + return default + raise def _unique(iterable: Iterable[T]) -> list[T]: From 6db3f5647b04dcb29daced478a32f566a9fb0d6e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:22:33 +0000 Subject: [PATCH 081/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/guild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index b7c3a8759c..e13f8db815 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -63,7 +63,7 @@ VoiceRegion, try_enum, ) -from .errors import ClientException, InvalidArgument, InvalidData, HTTPException +from .errors import ClientException, HTTPException, InvalidArgument, InvalidData from .file import File from .flags import SystemChannelFlags from .integrations import Integration, _integration_factory From 890cc53ff01a5ac648381a3ac607dfbe0d1b05d9 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 3 Aug 2025 11:53:14 +0200 Subject: [PATCH 082/111] Update discord/utils.py Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 5e54c2d57e..bdbc3c02f8 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -712,7 +712,7 @@ async def get_or_fetch( mapped_type = string_to_type.get(deprecated_attr.lower()) if mapped_type is None: raise InvalidArgument( - f"Unknown type string '{deprecated_attr}' used. Please use a valid object class like `discord.Member` instead." + f"Unknown type string '{deprecated_attr}' used. Please use a valid class like `discord.Member` instead." ) object_type = mapped_type elif isinstance(deprecated_attr, type): From f6d620698373f3694d6fd57efa5d4ae73b37ea52 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 3 Aug 2025 11:55:55 +0200 Subject: [PATCH 083/111] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a86a7e3174..4e224302af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -170,7 +170,7 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated `utils.get_or_fetch(attr, id)` in favor of +- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor of `utils.get_or_fetch(object_type, object_id)`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) From d6daa2d34a152eae8099b8983cd568064516b9bc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:56:23 +0000 Subject: [PATCH 084/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e224302af..253c13e547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -170,8 +170,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor of - `utils.get_or_fetch(object_type, object_id)`. +- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor + of `utils.get_or_fetch(object_type, object_id)`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ### Removed From 11035ec3b74b7c4fa30efa124ec067f0e3dc5f85 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 3 Aug 2025 12:27:21 +0200 Subject: [PATCH 085/111] paillat suggestions, in test --- discord/utils.py | 90 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index bdbc3c02f8..b646a93f14 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -600,6 +600,8 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: bound="VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Member | User | Guild | Role | GuildEmoji | AppEmoji", ) _D = TypeVar("_D") +_Getter = Callable[[Any, int], Any] +_Fetcher = Callable[[Any, int], Awaitable[Any]] # TODO: In version 3.0, remove the 'attr' and 'id' arguments. @@ -681,20 +683,11 @@ async def get_or_fetch( :exc:`Forbidden` You do not have permission to fetch the object. """ - from discord import AppEmoji, Client, Guild, Member, Role, User, abc, emoji + from discord import Client, Guild, Member, Role, User if object_id is None: return default if default is not MISSING else None - string_to_type = { - "channel": abc.GuildChannel, - "member": Member, - "user": User, - "guild": Guild, - "emoji": emoji._EmojiTag, - "appemoji": AppEmoji, - "role": Role, - } # Temporary backward compatibility for 'attr' and 'id'. # This entire if block should be removed in version 3.0. if attr is not MISSING or id is not MISSING or isinstance(object_type, str): @@ -709,7 +702,7 @@ async def get_or_fetch( deprecated_id = id if id is not MISSING else object_id if isinstance(deprecated_attr, str): - mapped_type = string_to_type.get(deprecated_attr.lower()) + mapped_type = _get_string_to_type_map().get(deprecated_attr.lower()) if mapped_type is None: raise InvalidArgument( f"Unknown type string '{deprecated_attr}' used. Please use a valid class like `discord.Member` instead." @@ -738,7 +731,47 @@ async def get_or_fetch( elif isinstance(obj, Guild) and object_type is Guild: raise InvalidArgument("Guild cannot get_or_fetch Guild. Use Client instead.") - getter_fetcher_map = { + try: + getter, fetcher = _get_getter_fetcher_map()[object_type] + except KeyError: + raise InvalidArgument( + f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" + ) + + result = getter(obj, object_id) + if result is not None: + return result + + try: + return await fetcher(obj, object_id) + except (HTTPException, ValueError): + if default is not MISSING: + return default + raise + + +@functools.lru_cache(maxsize=1) +def _get_string_to_type_map() -> dict[str, type]: + """Return a cached map of lowercase strings -> discord types.""" + from discord import Guild, Member, Role, User, abc, emoji + + return { + "channel": abc.GuildChannel, + "member": Member, + "user": User, + "guild": Guild, + "emoji": emoji._EmojiTag, + "appemoji": AppEmoji, + "role": Role, + } + + +@functools.lru_cache(maxsize=1) +def _get_getter_fetcher_map() -> dict[type, tuple[_Getter, _Fetcher]]: + """Return a cached map of type names -> (getter, fetcher) functions.""" + from discord import Guild, Member, Role, User, abc, emoji + + base_map: dict[type, tuple[_Getter, _Fetcher]] = { Member: ( lambda obj, oid: obj.get_member(oid), lambda obj, oid: obj.fetch_member(oid), @@ -764,26 +797,23 @@ async def get_or_fetch( lambda obj, oid: obj.fetch_channel(oid), ), } - try: - base_type = next( - base for base in getter_fetcher_map if issubclass(object_type, base) - ) - getter, fetcher = getter_fetcher_map[base_type] - except KeyError: - raise InvalidArgument( - f"Class {object_type.__name__} cannot be used with discord.{type(obj).__name__}.get_or_fetch()" - ) - result = getter(obj, object_id) - if result is not None: - return result + expanded: dict[type, tuple[_Getter, _Fetcher]] = {} + for base, funcs in base_map.items(): + expanded[base] = funcs + for subclass in _all_subclasses(base): + if subclass not in expanded: + expanded[subclass] = funcs - try: - return await fetcher(obj, object_id) - except (HTTPException, ValueError): - if default is not MISSING: - return default - raise + return expanded + + +def _all_subclasses(cls: type) -> set[type]: + """Recursively collect all subclasses of a class.""" + subs = set(cls.__subclasses__()) + for sub in cls.__subclasses__(): + subs |= _all_subclasses(sub) + return subs def _unique(iterable: Iterable[T]) -> list[T]: From 2d7fb85f6f5ab8ced0b86ee37068f4b9eaf8b46d Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 17 Aug 2025 23:07:04 +0200 Subject: [PATCH 086/111] fix bug invalid data unhandle --- discord/client.py | 8 ++++---- discord/guild.py | 2 +- discord/utils.py | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/discord/client.py b/discord/client.py index bd83326dbd..94124ef95d 100644 --- a/discord/client.py +++ b/discord/client.py @@ -247,9 +247,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( - {} - ) + self._listeners: dict[ + str, list[tuple[asyncio.Future, Callable[..., bool]]] + ] = {} self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") @@ -1223,7 +1223,7 @@ async def get_or_fetch( object_id=object_id, default=default, ) - except (HTTPException, ValueError): + except (HTTPException, ValueError, InvalidData): return default # listeners/waiters diff --git a/discord/guild.py b/discord/guild.py index e13f8db815..366ed3c1ad 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -904,7 +904,7 @@ async def get_or_fetch( object_id=object_id, default=default, ) - except (HTTPException, ValueError): + except (HTTPException, ValueError, InvalidData): return default @property diff --git a/discord/utils.py b/discord/utils.py index b646a93f14..0882ea44be 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -80,7 +80,7 @@ AppEmoji, ) -from .errors import HTTPException, InvalidArgument +from .errors import HTTPException, InvalidArgument, InvalidData try: import msgspec @@ -682,6 +682,8 @@ async def get_or_fetch( An error occurred fetching the object. :exc:`Forbidden` You do not have permission to fetch the object. + :exc:`InvalidData` + Raised when the object resolves to a different guild. """ from discord import Client, Guild, Member, Role, User @@ -744,7 +746,7 @@ async def get_or_fetch( try: return await fetcher(obj, object_id) - except (HTTPException, ValueError): + except (HTTPException, ValueError, InvalidData): if default is not MISSING: return default raise From 0ab70fdff811ce70ca9e7ec804a41853c6d24647 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:07:36 +0000 Subject: [PATCH 087/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 94124ef95d..628281857b 100644 --- a/discord/client.py +++ b/discord/client.py @@ -247,9 +247,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") From 3b98cac49e43d8976dca471e8574c0a8c6106eea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 15:48:47 +0000 Subject: [PATCH 088/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index c63ff9ca54..4b4087cc37 100644 --- a/discord/client.py +++ b/discord/client.py @@ -88,8 +88,8 @@ from .member import Member from .message import Message from .poll import Poll - from .threads import Thread, ThreadMember from .soundboard import SoundboardSound + from .threads import Thread, ThreadMember from .ui.item import Item from .voice_client import VoiceProtocol From c966f50efeed28ca4234912f20b5d25873588ceb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:53:27 +0000 Subject: [PATCH 089/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/discord/client.py b/discord/client.py index 11363dbae9..1aaffc67e4 100644 --- a/discord/client.py +++ b/discord/client.py @@ -85,9 +85,7 @@ VoiceChannel, ) from .interaction import Interaction - from .channel import DMChannel from .interactions import Interaction - from .member import Member from .message import Message from .poll import Poll From 9e9f5e754eda7b04029be334ddfce66abb70e60a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 1 Sep 2025 18:28:41 +0200 Subject: [PATCH 090/111] fix: changelog entry position Signed-off-by: Lala Sabathil --- CHANGELOG.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 637afc8dab..a4fbb5aba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Added +- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods. + ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) + ### Changed ### Fixed @@ -19,6 +22,12 @@ These changes are available on the `master` branch, but have not yet been releas - Manage silence for new SSRC with existing user_id. ([#2808](https://github.com/Pycord-Development/pycord/pull/2808)) +### Deprecated + +- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor + of `utils.get_or_fetch(object_type, object_id)`. + ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) + ### Removed ## [2.7.0rc1] - 2025-08-30 @@ -87,8 +96,6 @@ These changes are available on the `master` branch, but have not yet been releas ([#2775](https://github.com/Pycord-Development/pycord/pull/2775)) - Added `discord.Interaction.created_at`. ([#2801](https://github.com/Pycord-Development/pycord/pull/2801)) -- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods. - ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) - Added `User.nameplate` property. ([#2817](https://github.com/Pycord-Development/pycord/pull/2817)) - Added role gradients support with `Role.colours` and the `RoleColours` class. @@ -217,9 +224,6 @@ These changes are available on the `master` branch, but have not yet been releas ([#2501](https://github.com/Pycord-Development/pycord/pull/2501)) - Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`. ([#2658](https://github.com/Pycord-Development/pycord/pull/2658)) -- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor - of `utils.get_or_fetch(object_type, object_id)`. - ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) - Deprecated `is_nsfw` for categories since it was never supported by the API. ([#2772](https://github.com/Pycord-Development/pycord/pull/2772)) - Deprecated `Messageable.pins()` returning a list of `Message`; it should be used as an From 6c40af9d5922873db0ce2732f9e9fbc88f3d9ea9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:08:08 +0000 Subject: [PATCH 091/111] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a918024e..08f50b0782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ These changes are available on the `master` branch, but have not yet been releas message objects ([#2780](https://github.com/Pycord-Development/pycord/pull/2780)) - Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) - + ### Changed ### Fixed From 4a6cde06a6d444dedd664d5ce4a32ee707b21eed Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 6 Sep 2025 17:22:32 +0200 Subject: [PATCH 092/111] docs rewrits --- discord/client.py | 12 +++++++----- discord/guild.py | 12 ++++++------ discord/utils.py | 14 +++++++------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/discord/client.py b/discord/client.py index 1aaffc67e4..0b27579c6a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -1216,16 +1216,18 @@ async def get_or_fetch( Parameters ---------- - object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji + object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji` Type of object to fetch or get. - object_id: int | None - ID of object to get. If None, returns default if provided, else None. - default: Any | None + + object_id: :class:`int` | :data:`None` + ID of object to get. If :data:`None`, returns `default` if provided, else :data:`None`. + + default: Any | :data:`None` A default to return instead of raising if fetch fails. Returns ------- - VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | GuildEmoji | AppEmoji | None + :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji` | :data:`None` The object if found, or `default` if provided when not found. Raises diff --git a/discord/guild.py b/discord/guild.py index 56f44fb3cd..9d7ef7ee52 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1040,18 +1040,18 @@ async def get_or_fetch( Parameters ---------- - object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji + object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` Type of object to fetch or get. - object_id: :class:`int` | None - ID of the object to get. If ``None``, returns ``default`` if provided, otherwise ``None``. + object_id: :class:`int` | :data:`None` + ID of the object to get. If :data:`None`, returns `default` if provided, otherwise :data:`None`. - default : Any | None - The value to return instead of raising if fetching fails or if ``object_id`` is ``None``. + default : Any | :data:`None` + The value to return instead of raising if fetching fails or if `object_id` is :data:`None`. Returns ------- - VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | Role | Member | GuildEmoji | None + :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :data:`None` The object if found, or `default` if provided when not found. Raises diff --git a/discord/utils.py b/discord/utils.py index 00b1dc0f94..33c6461e8f 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -641,23 +641,23 @@ async def get_or_fetch( Parameters ---------- - obj : Guild | Client + obj : :class:`Guild` | :class:`Client` The object to operate on. - object_type: VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji + + object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :class:`AppEmoji` Type of object to fetch or get. - object_id: int | None + object_id: :class:`int` | :data:`None` ID of object to get. - default : Any | None + default : Any | :data:`None` The value to return instead of raising if fetching fails. Returns ------- - - VoiceChannel | TextChannel | ForumChannel | StageChannel | CategoryChannel | Thread | User | Guild | Role | Member | GuildEmoji | AppEmoji | None + :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :class:`AppEmoji` | :data:`None` The object if found, or `default` if provided when not found. - Returns `None` only if `object_id` is None and no `default` is given. + Returns :data:`None` only if `object_id` is :data:`None` and no `default` is given. Raises ------ From a446fa73f2f5e1301015a0f682f69157c692fad8 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:00:06 +0200 Subject: [PATCH 093/111] Update guild.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index 9d7ef7ee52..b55bc05ee1 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1046,7 +1046,7 @@ async def get_or_fetch( object_id: :class:`int` | :data:`None` ID of the object to get. If :data:`None`, returns `default` if provided, otherwise :data:`None`. - default : Any | :data:`None` + default: Any | :data:`None` The value to return instead of raising if fetching fails or if `object_id` is :data:`None`. Returns From c7edcf3da8a2de48e752b3425e0f1c792433d8fa Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:00:39 +0200 Subject: [PATCH 094/111] Update guild.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/guild.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index b55bc05ee1..747a7d7a98 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -3050,7 +3050,7 @@ def get_emoji(self, emoji_id: int, /) -> GuildEmoji | None: Parameters ---------- emoji_id: int - The ID to search for. + The ID to get. Returns ------- From 58d355f467aed15214714139ebe707ce67cbfcf7 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:00:51 +0200 Subject: [PATCH 095/111] Update utils.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index 33c6461e8f..e14d524667 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -641,7 +641,7 @@ async def get_or_fetch( Parameters ---------- - obj : :class:`Guild` | :class:`Client` + obj: :class:`Guild` | :class:`Client` The object to operate on. object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :class:`AppEmoji` @@ -650,7 +650,7 @@ async def get_or_fetch( object_id: :class:`int` | :data:`None` ID of object to get. - default : Any | :data:`None` + default: Any | :data:`None` The value to return instead of raising if fetching fails. Returns From adf9fbf26f3631b2d0c03495f617e74cbd2eaa7c Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:06:32 +0200 Subject: [PATCH 096/111] Update CHANGELOG.md Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5757913721..e2377b39f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ These changes are available on the `master` branch, but have not yet been releas ### Deprecated -- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favor +- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favour of `utils.get_or_fetch(object_type, object_id)`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) From 18f7dcf236b1c343d4ffd16f43d2bcae6dc48292 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:50:44 +0200 Subject: [PATCH 097/111] Update discord/client.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index 0b27579c6a..07513799f2 100644 --- a/discord/client.py +++ b/discord/client.py @@ -85,7 +85,6 @@ VoiceChannel, ) from .interaction import Interaction - from .interactions import Interaction from .member import Member from .message import Message from .poll import Poll From 6648fd5c77e26f6f1f01af5d39ad4301585c3eb6 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:51:01 +0200 Subject: [PATCH 098/111] fix copilot mistakes --- discord/client.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/discord/client.py b/discord/client.py index 0b27579c6a..74ec97a6a3 100644 --- a/discord/client.py +++ b/discord/client.py @@ -84,7 +84,6 @@ TextChannel, VoiceChannel, ) - from .interaction import Interaction from .interactions import Interaction from .member import Member from .message import Message @@ -250,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( - {} - ) + self._listeners: dict[ + str, list[tuple[asyncio.Future, Callable[..., bool]]] + ] = {} self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") From c4c5459ddd5dcbeef6f5ba4ddd4d0979351c63f4 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:52:05 +0200 Subject: [PATCH 099/111] fix --- discord/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 74ec97a6a3..8f94af634a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -249,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") From ae36e61c4706db37eb93eaf3f8c06a434338d947 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:53:38 +0000 Subject: [PATCH 100/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 74ec97a6a3..8f94af634a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -249,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") From 154d98a1c388cc96dffe8797c5aa90021919a235 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:59:33 +0200 Subject: [PATCH 101/111] usage of type for specification --- discord/client.py | 8 ++++---- discord/guild.py | 2 +- discord/utils.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/discord/client.py b/discord/client.py index 8f94af634a..5c24d4b5f4 100644 --- a/discord/client.py +++ b/discord/client.py @@ -249,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( - {} - ) + self._listeners: dict[ + str, list[tuple[asyncio.Future, Callable[..., bool]]] + ] = {} self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") @@ -1215,7 +1215,7 @@ async def get_or_fetch( Parameters ---------- - object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji` + object_type: Type[:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji`] Type of object to fetch or get. object_id: :class:`int` | :data:`None` diff --git a/discord/guild.py b/discord/guild.py index 747a7d7a98..2b969faeaa 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1040,7 +1040,7 @@ async def get_or_fetch( Parameters ---------- - object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` + object_type: Type[:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji`] Type of object to fetch or get. object_id: :class:`int` | :data:`None` diff --git a/discord/utils.py b/discord/utils.py index e14d524667..473c69843e 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -641,10 +641,10 @@ async def get_or_fetch( Parameters ---------- - obj: :class:`Guild` | :class:`Client` + obj: :class:`~discord.Guild` | :class:`~discord.Client` The object to operate on. - object_type: :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :class:`AppEmoji` + object_type: Type[:class:`~discord.VoiceChannel` | :class:`~discord.TextChannel` | :class:`~discord.ForumChannel` | :class:`~discord.StageChannel` | :class:`~discord.CategoryChannel` | :class:`~discord.Thread` | :class:`~discord.User` | :class:`~discord.Guild` | :class:`~discord.Role` | :class:`~discord.Member` | :class:`~discord.GuildEmoji` | :class:`~discord.AppEmoji`] Type of object to fetch or get. object_id: :class:`int` | :data:`None` @@ -655,7 +655,7 @@ async def get_or_fetch( Returns ------- - :class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :class:`AppEmoji` | :data:`None` + :class:`~discord.VoiceChannel` | :class:`~discord.TextChannel` | :class:`~discord.ForumChannel` | :class:`~discord.StageChannel` | :class:`~discord.CategoryChannel` | :class:`~discord.Thread` | :class:`~discord.User` | :class:`~discord.Guild` | :class:`~discord.Role` | :class:`~discord.Member` | :class:`~discord.GuildEmoji` | :class:`~discord.AppEmoji` | :data:`None` The object if found, or `default` if provided when not found. Returns :data:`None` only if `object_id` is :data:`None` and no `default` is given. From 3c6b526e518c2ece2604bb37df364af58b66a2f7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:00:37 +0000 Subject: [PATCH 102/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index 5c24d4b5f4..76eb0d9f9a 100644 --- a/discord/client.py +++ b/discord/client.py @@ -249,9 +249,9 @@ def __init__( self.loop: asyncio.AbstractEventLoop = ( asyncio.get_event_loop() if loop is None else loop ) - self._listeners: dict[ - str, list[tuple[asyncio.Future, Callable[..., bool]]] - ] = {} + self._listeners: dict[str, list[tuple[asyncio.Future, Callable[..., bool]]]] = ( + {} + ) self.shard_id: int | None = options.get("shard_id") self.shard_count: int | None = options.get("shard_count") From 58b92f30d7360a6f25a8a761e9863473937430d7 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 11 Oct 2025 08:23:38 +0200 Subject: [PATCH 103/111] Update discord/utils.py Co-authored-by: Soheab <33902984+Soheab@users.noreply.github.com> Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 473c69843e..0d1bbd36b8 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -769,7 +769,7 @@ def _get_getter_fetcher_map() -> dict[type, tuple[_Getter, _Fetcher]]: ), Role: ( lambda obj, oid: obj.get_role(oid), - lambda obj, oid: obj._fetch_role(oid), + lambda obj, oid: obj.fetch_role(oid), ), User: ( lambda obj, oid: obj.get_user(oid), From 62401738d50d6d69578f733c581fe01352d1f000 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 11 Oct 2025 09:42:36 +0300 Subject: [PATCH 104/111] fix: :bug: Prevent `InvalidArgument` for `AppEmoji` in `get_or_fetch` method --- discord/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discord/utils.py b/discord/utils.py index 0d1bbd36b8..579fce82e3 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -721,6 +721,8 @@ async def get_or_fetch( raise InvalidArgument("Client cannot get_or_fetch Role. Use Guild instead.") elif isinstance(obj, Guild) and object_type is Guild: raise InvalidArgument("Guild cannot get_or_fetch Guild. Use Client instead.") + elif isinstance(obj, Guild) and object_type is AppEmoji: + raise InvalidArgument("Guild cannot get_or_fetch AppEmoji. Use Client instead.") try: getter, fetcher = _get_getter_fetcher_map()[object_type] From 9fdcad4c0e02f4590cea9e793beab599bd31e909 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sat, 11 Oct 2025 09:44:03 +0300 Subject: [PATCH 105/111] fix: :bug: Import `AppEmoji` in `get_or_fetch` to resolve potential issues --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 579fce82e3..46e33fc0c6 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -674,7 +674,7 @@ async def get_or_fetch( :exc:`InvalidData` Raised when the object resolves to a different guild. """ - from discord import Client, Guild, Member, Role, User + from discord import Client, Guild, Member, Role, User, AppEmoji if object_id is None: return default if default is not MISSING else None From 316e07d540a9d2ca129fc83a9c7a8bd3c1b83a19 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 06:44:32 +0000 Subject: [PATCH 106/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 46e33fc0c6..e1f21cb892 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -674,7 +674,7 @@ async def get_or_fetch( :exc:`InvalidData` Raised when the object resolves to a different guild. """ - from discord import Client, Guild, Member, Role, User, AppEmoji + from discord import AppEmoji, Client, Guild, Member, Role, User if object_id is None: return default if default is not MISSING else None From 10b5bbdb4d7dc27ac4311cf8b4ad50a9a748e2b7 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 19 Oct 2025 17:42:24 +0200 Subject: [PATCH 107/111] Update CHANGELOG.md Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1537b9e74b..983fb6bf1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ These changes are available on the `master` branch, but have not yet been releas ### Deprecated - Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favour - of `utils.get_or_fetch(object_type, object_id)`. + of `utils.get_or_fetch(object_type, object_id)` and `Client.get_or_fetch(User, id)`. ([#2776](https://github.com/Pycord-Development/pycord/pull/2776)) ### Removed From 3c89f0771381ae8e6e588105866d45e5394bea8e Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 19 Oct 2025 17:42:51 +0200 Subject: [PATCH 108/111] Update utils.py Co-authored-by: Paillat Signed-off-by: Lumouille <144063653+Lumabots@users.noreply.github.com> --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index e1f21cb892..d646c72a4b 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -713,7 +713,7 @@ async def get_or_fetch( if isinstance(obj, Guild) and object_type is User: raise InvalidArgument( - "Guild cannot get_or_fetch discord.User. Use Client instead." + "Guild cannot get_or_fetch User. Use Client instead." ) elif isinstance(obj, Client) and object_type is Member: raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") From 2b14eceeaab7ad5806d7dbabaa425d52016af351 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:43:19 +0000 Subject: [PATCH 109/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index d646c72a4b..238bf88d8a 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -712,9 +712,7 @@ async def get_or_fetch( raise TypeError("required parameters: `object_type` and `object_id`.") if isinstance(obj, Guild) and object_type is User: - raise InvalidArgument( - "Guild cannot get_or_fetch User. Use Client instead." - ) + raise InvalidArgument("Guild cannot get_or_fetch User. Use Client instead.") elif isinstance(obj, Client) and object_type is Member: raise InvalidArgument("Client cannot get_or_fetch Member. Use Guild instead.") elif isinstance(obj, Client) and object_type is Role: From 82e11f30b388bbc7e7eb34005d9870fc363f0756 Mon Sep 17 00:00:00 2001 From: Lumouille <144063653+Lumabots@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:29:48 +0300 Subject: [PATCH 110/111] fix: :bug: Import `AppEmoji` in `_get_string_to_type_map` for type mapping --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index e1f21cb892..1bffe68ca0 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -746,7 +746,7 @@ async def get_or_fetch( @functools.lru_cache(maxsize=1) def _get_string_to_type_map() -> dict[str, type]: """Return a cached map of lowercase strings -> discord types.""" - from discord import Guild, Member, Role, User, abc, emoji + from discord import Guild, Member, Role, User, abc, emoji, AppEmoji return { "channel": abc.GuildChannel, From 915e16478cbcb8faef6b99b10789b3fd853ea139 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 17:30:42 +0000 Subject: [PATCH 111/111] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/utils.py b/discord/utils.py index 0e303290f7..37b7c5ffdf 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -744,7 +744,7 @@ async def get_or_fetch( @functools.lru_cache(maxsize=1) def _get_string_to_type_map() -> dict[str, type]: """Return a cached map of lowercase strings -> discord types.""" - from discord import Guild, Member, Role, User, abc, emoji, AppEmoji + from discord import AppEmoji, Guild, Member, Role, User, abc, emoji return { "channel": abc.GuildChannel,