Skip to content

Commit b9bfe00

Browse files
Lumabotspre-commit-ci[bot]plun1331Paillat-devLulalaby
authored
feat: Implement better get_or_fetch (#2776)
* Update CHANGELOG.md Signed-off-by: Lumouille <[email protected]> * add a get or fetch to guild Signed-off-by: Lumouille <[email protected]> * Implement better get_or_fetch using typevar and object Signed-off-by: Lumouille <[email protected]> * shortcut for get or fetch Signed-off-by: Lumouille <[email protected]> * Update CHANGELOG.md Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * usage of subclass Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * add get_emoji method Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * add get_or_fetch_emoji method Signed-off-by: Lumouille <[email protected]> * shortcut getorfetch client + removal of get_or_fetch user in favor of get_or_fetch shortcut Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update utils.py Signed-off-by: Lumouille <[email protected]> * Update guild.py Signed-off-by: Lumouille <[email protected]> * Update client.py Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update utils.py Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * add utils.deprecated Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * add warning Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update client.py Signed-off-by: Lumouille <[email protected]> * added backward support Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * fixed missing coma Signed-off-by: Lumouille <[email protected]> * ig im drunk Signed-off-by: Lumouille <[email protected]> * fix my drunkness Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * now im not drunk Signed-off-by: Lumouille <[email protected]> * Update utils.py Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update utils.py Signed-off-by: Lumouille <[email protected]> * Update utils.py Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * usage of abc Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * _EmojiTag Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update utils.py Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update utils.py Signed-off-by: Lumouille <[email protected]> * Update discord/client.py Co-authored-by: plun1331 <[email protected]> Signed-off-by: Lumouille <[email protected]> * add the raise error Signed-off-by: Lumouille <[email protected]> * Update discord/utils.py Co-authored-by: plun1331 <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update utils.pyfix missing appemoji Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * add missing raise to doc * refactor: add TODO comments for removal of deprecated arguments in get_or_fetch_user and get_or_fetch * refactor: move _FETCHABLE type variable to utils for better accessibility * fix: update import statement for _FETCHABLE from utils module * refactor: streamline get_or_fetch logic with a mapping for object types * fix: add validation for Guild type in get_or_fetch function * fix: correct base type resolution in get_or_fetch function * fix: add Role type support in get_or_fetch function * fix: update role retrieval methods in get_or_fetch function * Update CHANGELOG.md Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update CHANGELOG.md Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update CHANGELOG.md Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * Update CHANGELOG.md Co-authored-by: Paillat <[email protected]> Signed-off-by: Lumouille <[email protected]> * feat: update get_or_fetch method to accept Optional[int] for object_id * fix: "MISSING" not being exported * add some more comment * Update utils.py * style(pre-commit): auto fixes from pre-commit.com hooks * Update discord/utils.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update discord/utils.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * comment * style(pre-commit): auto fixes from pre-commit.com hooks * fix docs * Update discord/client.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * usage of literal None * style(pre-commit): auto fixes from pre-commit.com hooks * return None when fetching fails * style(pre-commit): auto fixes from pre-commit.com hooks * back to non breaking * style(pre-commit): auto fixes from pre-commit.com hooks * Update discord/utils.py Co-authored-by: Paillat <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update CHANGELOG.md * style(pre-commit): auto fixes from pre-commit.com hooks * paillat suggestions, in test * fix bug invalid data unhandle * style(pre-commit): auto fixes from pre-commit.com hooks * style(pre-commit): auto fixes from pre-commit.com hooks * style(pre-commit): auto fixes from pre-commit.com hooks * fix: changelog entry position Signed-off-by: Lala Sabathil <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * docs rewrits * Update guild.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update guild.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update utils.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update CHANGELOG.md Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update discord/client.py Co-authored-by: Copilot <[email protected]> Signed-off-by: Lumouille <[email protected]> * fix copilot mistakes * fix * style(pre-commit): auto fixes from pre-commit.com hooks * usage of type for specification * style(pre-commit): auto fixes from pre-commit.com hooks * Update discord/utils.py Co-authored-by: Soheab <[email protected]> Signed-off-by: Lumouille <[email protected]> * fix: 🐛 Prevent `InvalidArgument` for `AppEmoji` in `get_or_fetch` method * fix: 🐛 Import `AppEmoji` in `get_or_fetch` to resolve potential issues * style(pre-commit): auto fixes from pre-commit.com hooks * Update CHANGELOG.md Co-authored-by: Paillat <[email protected]> Signed-off-by: Lumouille <[email protected]> * Update utils.py Co-authored-by: Paillat <[email protected]> Signed-off-by: Lumouille <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * fix: 🐛 Import `AppEmoji` in `_get_string_to_type_map` for type mapping * style(pre-commit): auto fixes from pre-commit.com hooks --------- Signed-off-by: Lumouille <[email protected]> Signed-off-by: Lala Sabathil <[email protected]> Signed-off-by: Soheab <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: plun1331 <[email protected]> Co-authored-by: Paillat <[email protected]> Co-authored-by: Lala Sabathil <[email protected]> Co-authored-by: Soheab <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Paillat <[email protected]>
1 parent 40656fd commit b9bfe00

File tree

4 files changed

+363
-47
lines changed

4 files changed

+363
-47
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ These changes are available on the `master` branch, but have not yet been releas
1717
([#2711](https://github.com/Pycord-Development/pycord/pull/2711))
1818
- Added `RawMessageUpdateEvent.new_message` - message update events now contain full
1919
message objects ([#2780](https://github.com/Pycord-Development/pycord/pull/2780))
20+
- Added `Guild.get_or_fetch()` and `Client.get_or_fetch()` shortcut methods.
21+
([#2776](https://github.com/Pycord-Development/pycord/pull/2776))
2022
- Added support for setting guild-specific `avatar`, `banner`, and `bio` for the bot
2123
user through `Member.edit`.
2224
([#2908](https://github.com/Pycord-Development/pycord/pull/2908))
@@ -57,6 +59,12 @@ These changes are available on the `master` branch, but have not yet been releas
5759
- Fixed `TypeError` when using Python 3.12+ `type` syntax for typing slash command
5860
parameters. ([#2952](https://github.com/Pycord-Development/pycord/pull/2952))
5961

62+
### Deprecated
63+
64+
- Deprecated `utils.get_or_fetch(attr, id)` and `Client.get_or_fetch_user(id)` in favour
65+
of `utils.get_or_fetch(object_type, object_id)` and `Client.get_or_fetch(User, id)`.
66+
([#2776](https://github.com/Pycord-Development/pycord/pull/2776))
67+
6068
### Removed
6169

6270
## [2.7.0rc1] - 2025-08-30

discord/client.py

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@
3131
import sys
3232
import traceback
3333
from types import TracebackType
34-
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar
34+
from typing import (
35+
TYPE_CHECKING,
36+
Any,
37+
Callable,
38+
Coroutine,
39+
Generator,
40+
Sequence,
41+
TypeVar,
42+
)
3543

3644
import aiohttp
3745

@@ -61,19 +69,27 @@
6169
from .threads import Thread
6270
from .ui.view import View
6371
from .user import ClientUser, User
64-
from .utils import MISSING
72+
from .utils import _D, _FETCHABLE, MISSING
6573
from .voice_client import VoiceClient
6674
from .webhook import Webhook
6775
from .widget import Widget
6876

6977
if TYPE_CHECKING:
7078
from .abc import GuildChannel, PrivateChannel, Snowflake, SnowflakeTime
71-
from .channel import DMChannel
79+
from .channel import (
80+
CategoryChannel,
81+
DMChannel,
82+
ForumChannel,
83+
StageChannel,
84+
TextChannel,
85+
VoiceChannel,
86+
)
7287
from .interactions import Interaction
7388
from .member import Member
7489
from .message import Message
7590
from .poll import Poll
7691
from .soundboard import SoundboardSound
92+
from .threads import Thread, ThreadMember
7793
from .ui.item import Item
7894
from .voice_client import VoiceProtocol
7995

@@ -1165,7 +1181,12 @@ def get_all_members(self) -> Generator[Member]:
11651181
for guild in self.guilds:
11661182
yield from guild.members
11671183

1168-
async def get_or_fetch_user(self, id: int, /) -> User | None:
1184+
@utils.deprecated(
1185+
instead="Client.get_or_fetch(User, id)",
1186+
since="2.7",
1187+
removed="3.0",
1188+
)
1189+
async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in 3.0
11691190
"""|coro|
11701191
11711192
Looks up a user in the user cache or fetches if not found.
@@ -1181,7 +1202,49 @@ async def get_or_fetch_user(self, id: int, /) -> User | None:
11811202
The user or ``None`` if not found.
11821203
"""
11831204

1184-
return await utils.get_or_fetch(obj=self, attr="user", id=id, default=None)
1205+
return await self.get_or_fetch(object_type=User, object_id=id, default=None)
1206+
1207+
async def get_or_fetch(
1208+
self: Client,
1209+
object_type: type[_FETCHABLE],
1210+
object_id: int | None,
1211+
default: _D = None,
1212+
) -> _FETCHABLE | _D | None:
1213+
"""
1214+
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.
1215+
1216+
Parameters
1217+
----------
1218+
object_type: Type[:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji`]
1219+
Type of object to fetch or get.
1220+
1221+
object_id: :class:`int` | :data:`None`
1222+
ID of object to get. If :data:`None`, returns `default` if provided, else :data:`None`.
1223+
1224+
default: Any | :data:`None`
1225+
A default to return instead of raising if fetch fails.
1226+
1227+
Returns
1228+
-------
1229+
:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`User` | :class:`Guild` | :class:`GuildEmoji` | :class:`AppEmoji` | :data:`None`
1230+
The object if found, or `default` if provided when not found.
1231+
1232+
Raises
1233+
------
1234+
:exc:`TypeError`
1235+
Raised when required parameters are missing or invalid types are provided.
1236+
:exc:`InvalidArgument`
1237+
Raised when an unsupported or incompatible object type is used.
1238+
"""
1239+
try:
1240+
return await utils.get_or_fetch(
1241+
obj=self,
1242+
object_type=object_type,
1243+
object_id=object_id,
1244+
default=default,
1245+
)
1246+
except (HTTPException, ValueError, InvalidData):
1247+
return default
11851248

11861249
# listeners/waiters
11871250

discord/guild.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
VoiceRegion,
6767
try_enum,
6868
)
69-
from .errors import ClientException, InvalidArgument, InvalidData
69+
from .errors import ClientException, HTTPException, InvalidArgument, InvalidData
7070
from .file import File
7171
from .flags import SystemChannelFlags
7272
from .incidents import IncidentsData
@@ -90,6 +90,7 @@
9090
from .sticker import GuildSticker
9191
from .threads import Thread, ThreadMember
9292
from .user import User
93+
from .utils import _D, _FETCHABLE
9394
from .welcome_screen import WelcomeScreen, WelcomeScreenChannel
9495
from .widget import Widget
9596

@@ -1041,6 +1042,49 @@ def get_member(self, user_id: int, /) -> Member | None:
10411042
"""
10421043
return self._members.get(user_id)
10431044

1045+
async def get_or_fetch(
1046+
self: Guild,
1047+
object_type: type[_FETCHABLE],
1048+
object_id: int | None,
1049+
default: _D = None,
1050+
) -> _FETCHABLE | _D | None:
1051+
"""
1052+
Shortcut method to get data from this guild either by returning the cached version,
1053+
or if it does not exist, attempting to fetch it from the API.
1054+
1055+
Parameters
1056+
----------
1057+
object_type: Type[:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji`]
1058+
Type of object to fetch or get.
1059+
1060+
object_id: :class:`int` | :data:`None`
1061+
ID of the object to get. If :data:`None`, returns `default` if provided, otherwise :data:`None`.
1062+
1063+
default: Any | :data:`None`
1064+
The value to return instead of raising if fetching fails or if `object_id` is :data:`None`.
1065+
1066+
Returns
1067+
-------
1068+
:class:`VoiceChannel` | :class:`TextChannel` | :class:`ForumChannel` | :class:`StageChannel` | :class:`CategoryChannel` | :class:`Thread` | :class:`Role` | :class:`Member` | :class:`GuildEmoji` | :data:`None`
1069+
The object if found, or `default` if provided when not found.
1070+
1071+
Raises
1072+
------
1073+
:exc:`TypeError`
1074+
Raised when required parameters are missing or invalid types are provided.
1075+
:exc:`InvalidArgument`
1076+
Raised when an unsupported or incompatible object type is used.
1077+
"""
1078+
try:
1079+
return await utils.get_or_fetch(
1080+
obj=self,
1081+
object_type=object_type,
1082+
object_id=object_id,
1083+
default=default,
1084+
)
1085+
except (HTTPException, ValueError, InvalidData):
1086+
return default
1087+
10441088
@property
10451089
def premium_subscribers(self) -> list[Member]:
10461090
"""A list of members who have "boosted" this guild."""
@@ -3012,6 +3056,26 @@ async def delete_sticker(
30123056
"""
30133057
await self._state.http.delete_guild_sticker(self.id, sticker.id, reason)
30143058

3059+
def get_emoji(self, emoji_id: int, /) -> GuildEmoji | None:
3060+
"""Returns an emoji with the given ID.
3061+
3062+
.. versionadded:: 2.7
3063+
3064+
Parameters
3065+
----------
3066+
emoji_id: int
3067+
The ID to get.
3068+
3069+
Returns
3070+
-------
3071+
Optional[:class:`Emoji`]
3072+
The returned Emoji or ``None`` if not found.
3073+
"""
3074+
emoji = self._state.get_emoji(emoji_id)
3075+
if emoji and emoji.guild == self:
3076+
return emoji
3077+
return None
3078+
30153079
async def fetch_emojis(self) -> list[GuildEmoji]:
30163080
r"""|coro|
30173081

0 commit comments

Comments
 (0)