From c8733e4a1a1719b85bac85e0cb139dbf62cc1a94 Mon Sep 17 00:00:00 2001 From: Riccardo Vaccari Date: Sun, 27 Apr 2025 09:09:41 +0200 Subject: [PATCH 1/5] feat: Replaced useless `cached_property` with `property` and moved to `functools.cached_property` --- discord/audit_logs.py | 12 ++++++------ discord/automod.py | 4 ++-- discord/commands/context.py | 24 +++++++++++------------- discord/ext/commands/context.py | 8 ++++---- discord/onboarding.py | 4 ++-- discord/poll.py | 2 +- discord/utils.py | 19 ------------------- tests/test_utils.py | 17 ----------------- 8 files changed, 26 insertions(+), 64 deletions(-) diff --git a/discord/audit_logs.py b/discord/audit_logs.py index e2ff277dfe..6f872b30f8 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -602,12 +602,12 @@ def _get_member(self, user_id: int) -> Member | User | None: def __repr__(self) -> str: return f"" - @utils.cached_property + @property def created_at(self) -> datetime.datetime: """Returns the entry's creation time in UTC.""" return utils.snowflake_time(self.id) - @utils.cached_property + @property def target( self, ) -> ( @@ -631,24 +631,24 @@ def target( else: return converter(self._target_id) - @utils.cached_property + @property def category(self) -> enums.AuditLogActionCategory: """The category of the action, if applicable.""" return self.action.category - @utils.cached_property + @property def changes(self) -> AuditLogChanges: """The list of changes this entry has.""" obj = AuditLogChanges(self, self._changes, state=self._state) del self._changes return obj - @utils.cached_property + @property def before(self) -> AuditLogDiff: """The target's prior state.""" return self.changes.before - @utils.cached_property + @property def after(self) -> AuditLogDiff: """The target's subsequent state.""" return self.changes.after diff --git a/discord/automod.py b/discord/automod.py index c4b1e23c0e..b7bbfb3360 100644 --- a/discord/automod.py +++ b/discord/automod.py @@ -416,12 +416,12 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.name - @cached_property + @property def guild(self) -> Guild | None: """The guild this rule belongs to.""" return self._state._get_guild(self.guild_id) - @cached_property + @property def creator(self) -> Member | None: """The member who created this rule.""" if self.guild is None: diff --git a/discord/commands/context.py b/discord/commands/context.py index 532d8abe2a..15830031ae 100644 --- a/discord/commands/context.py +++ b/discord/commands/context.py @@ -53,8 +53,6 @@ from typing import Callable, Awaitable -from ..utils import cached_property - T = TypeVar("T") CogT = TypeVar("CogT", bound="Cog") @@ -136,53 +134,53 @@ async def invoke( """ return await command(self, *args, **kwargs) - @cached_property + @property def channel(self) -> InteractionChannel | None: """Union[:class:`abc.GuildChannel`, :class:`PartialMessageable`, :class:`Thread`]: Returns the channel associated with this context's command. Shorthand for :attr:`.Interaction.channel`. """ return self.interaction.channel - @cached_property + @property def channel_id(self) -> int | None: """Returns the ID of the channel associated with this context's command. Shorthand for :attr:`.Interaction.channel_id`. """ return self.interaction.channel_id - @cached_property + @property def guild(self) -> Guild | None: """Returns the guild associated with this context's command. Shorthand for :attr:`.Interaction.guild`. """ return self.interaction.guild - @cached_property + @property def guild_id(self) -> int | None: """Returns the ID of the guild associated with this context's command. Shorthand for :attr:`.Interaction.guild_id`. """ return self.interaction.guild_id - @cached_property + @property def locale(self) -> str | None: """Returns the locale of the guild associated with this context's command. Shorthand for :attr:`.Interaction.locale`. """ return self.interaction.locale - @cached_property + @property def guild_locale(self) -> str | None: """Returns the locale of the guild associated with this context's command. Shorthand for :attr:`.Interaction.guild_locale`. """ return self.interaction.guild_locale - @cached_property + @property def app_permissions(self) -> Permissions: return self.interaction.app_permissions - @cached_property + @property def me(self) -> Member | ClientUser | None: """Union[:class:`.Member`, :class:`.ClientUser`]: Similar to :attr:`.Guild.me` except it may return the :class:`.ClientUser` in private message @@ -194,14 +192,14 @@ def me(self) -> Member | ClientUser | None: else self.bot.user ) - @cached_property + @property def message(self) -> Message | None: """Returns the message sent with this context's command. Shorthand for :attr:`.Interaction.message`, if applicable. """ return self.interaction.message - @cached_property + @property def user(self) -> Member | User: """Returns the user that sent this context's command. Shorthand for :attr:`.Interaction.user`. @@ -220,7 +218,7 @@ def voice_client(self) -> VoiceClient | None: return self.interaction.guild.voice_client - @cached_property + @property def response(self) -> InteractionResponse: """Returns the response object associated with this context's command. Shorthand for :attr:`.Interaction.response`. diff --git a/discord/ext/commands/context.py b/discord/ext/commands/context.py index c140586faa..4ab081ba23 100644 --- a/discord/ext/commands/context.py +++ b/discord/ext/commands/context.py @@ -283,28 +283,28 @@ def cog(self) -> Cog | None: return None return self.command.cog - @discord.utils.cached_property + @property def guild(self) -> Guild | None: """Returns the guild associated with this context's command. None if not available. """ return self.message.guild - @discord.utils.cached_property + @property def channel(self) -> MessageableChannel: """Returns the channel associated with this context's command. Shorthand for :attr:`.Message.channel`. """ return self.message.channel - @discord.utils.cached_property + @property def author(self) -> User | Member: """Union[:class:`~discord.User`, :class:`.Member`]: Returns the author associated with this context's command. Shorthand for :attr:`.Message.author` """ return self.message.author - @discord.utils.cached_property + @property def me(self) -> Member | ClientUser: """Union[:class:`.Member`, :class:`.ClientUser`]: Similar to :attr:`.Guild.me` except it may return the :class:`.ClientUser` in private message diff --git a/discord/onboarding.py b/discord/onboarding.py index cf6a721a00..17b0bd8c83 100644 --- a/discord/onboarding.py +++ b/discord/onboarding.py @@ -28,7 +28,7 @@ from .enums import OnboardingMode, PromptType, try_enum from .partial_emoji import PartialEmoji -from .utils import MISSING, cached_property, generate_snowflake, get +from .utils import MISSING, generate_snowflake, get if TYPE_CHECKING: from .abc import Snowflake @@ -247,7 +247,7 @@ def _update(self, data: OnboardingPayload): self.enabled: bool = data["enabled"] self.mode: OnboardingMode = try_enum(OnboardingMode, data.get("mode")) - @cached_property + @property def default_channels( self, ) -> list[TextChannel | ForumChannel | VoiceChannel | Object]: diff --git a/discord/poll.py b/discord/poll.py index 087d2833f6..e78cfa2ebf 100644 --- a/discord/poll.py +++ b/discord/poll.py @@ -358,7 +358,7 @@ def __init__( self._expiry = None self._message = None - @utils.cached_property + @property def expiry(self) -> datetime.datetime | None: """An aware datetime object that specifies the date and time in UTC when the poll will end.""" return utils.parse_time(self._expiry) diff --git a/discord/utils.py b/discord/utils.py index 363d339391..3e02efddcc 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -121,22 +121,6 @@ def __repr__(self) -> str: # error. MissingField = field(default_factory=lambda: MISSING) - -class _cached_property: - def __init__(self, function): - self.function = function - self.__doc__ = getattr(function, "__doc__") - - def __get__(self, instance, owner): - if instance is None: - return self - - value = self.function(instance) - setattr(instance, self.function.__name__, value) - - return value - - if TYPE_CHECKING: from typing_extensions import ParamSpec @@ -150,12 +134,9 @@ def __get__(self, instance, owner): class _RequestLike(Protocol): headers: Mapping[str, Any] - cached_property = property - P = ParamSpec("P") else: - cached_property = _cached_property AutocompleteContext = Any OptionChoice = Any diff --git a/tests/test_utils.py b/tests/test_utils.py index d0f94acb93..86ccb3dfb2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -30,7 +30,6 @@ from discord.utils import ( MISSING, - _cached_property, _parse_ratelimit_header, _unique, async_all, @@ -80,22 +79,6 @@ def test_temporary(): # assert repr(MISSING) == '...' # # -# def test_cached_property() -> None: -# class Test: -# def __init__(self, x: int): -# self.x = x -# -# @_cached_property -# def foo(self) -> int: -# self.x += 1 -# return self.x -# -# t = Test(0) -# assert isinstance(_cached_property.__get__(_cached_property(None), None, None), _cached_property) -# assert t.foo == 1 -# assert t.foo == 1 -# -# # def test_find_get() -> None: # class Obj: # def __init__(self, value: int): From d119620b8df74c66caf1939c17bc12f0cc7a4897 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Apr 2025 07:20:48 +0000 Subject: [PATCH 2/5] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/commands/context.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/discord/commands/context.py b/discord/commands/context.py index 15830031ae..b53d56dd56 100644 --- a/discord/commands/context.py +++ b/discord/commands/context.py @@ -32,26 +32,25 @@ from discord.webhook.async_ import Webhook if TYPE_CHECKING: + from typing import Awaitable, Callable + from typing_extensions import ParamSpec import discord - from .. import Bot - from ..state import ConnectionState - from ..voice_client import VoiceClient - from .core import ApplicationCommand, Option - from ..interactions import InteractionChannel + from .. import Bot + from ..client import ClientUser + from ..cog import Cog from ..guild import Guild + from ..interactions import InteractionChannel from ..member import Member from ..message import Message - from ..user import User from ..permissions import Permissions - from ..client import ClientUser - - from ..cog import Cog + from ..state import ConnectionState + from ..user import User + from ..voice_client import VoiceClient from ..webhook import WebhookMessage - - from typing import Callable, Awaitable + from .core import ApplicationCommand, Option T = TypeVar("T") CogT = TypeVar("CogT", bound="Cog") From c9e9a37c1f8f1dc2dd7e899ad4e13228bdfbc08e Mon Sep 17 00:00:00 2001 From: Riccardo Vaccari Date: Tue, 20 May 2025 15:31:59 +0200 Subject: [PATCH 3/5] chore: switch to `functools.cached_property` for intensive code --- discord/onboarding.py | 3 ++- discord/poll.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/discord/onboarding.py b/discord/onboarding.py index 17b0bd8c83..e0c1a364a9 100644 --- a/discord/onboarding.py +++ b/discord/onboarding.py @@ -25,6 +25,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any +from functools import cached_property from .enums import OnboardingMode, PromptType, try_enum from .partial_emoji import PartialEmoji @@ -247,7 +248,7 @@ def _update(self, data: OnboardingPayload): self.enabled: bool = data["enabled"] self.mode: OnboardingMode = try_enum(OnboardingMode, data.get("mode")) - @property + @cached_property def default_channels( self, ) -> list[TextChannel | ForumChannel | VoiceChannel | Object]: diff --git a/discord/poll.py b/discord/poll.py index e78cfa2ebf..4d143f4da3 100644 --- a/discord/poll.py +++ b/discord/poll.py @@ -26,6 +26,7 @@ import datetime from typing import TYPE_CHECKING, Any +from functools import cached_property from . import utils from .enums import PollLayoutType, try_enum @@ -358,7 +359,7 @@ def __init__( self._expiry = None self._message = None - @property + @cached_property def expiry(self) -> datetime.datetime | None: """An aware datetime object that specifies the date and time in UTC when the poll will end.""" return utils.parse_time(self._expiry) From 0072af88610a9c30295081bacbeb9d4b1a233d6a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 13:42:17 +0000 Subject: [PATCH 4/5] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/onboarding.py | 2 +- discord/poll.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/onboarding.py b/discord/onboarding.py index e0c1a364a9..4e4fa7e993 100644 --- a/discord/onboarding.py +++ b/discord/onboarding.py @@ -24,8 +24,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any from functools import cached_property +from typing import TYPE_CHECKING, Any from .enums import OnboardingMode, PromptType, try_enum from .partial_emoji import PartialEmoji diff --git a/discord/poll.py b/discord/poll.py index 4d143f4da3..fcc94cc4b4 100644 --- a/discord/poll.py +++ b/discord/poll.py @@ -25,8 +25,8 @@ from __future__ import annotations import datetime -from typing import TYPE_CHECKING, Any from functools import cached_property +from typing import TYPE_CHECKING, Any from . import utils from .enums import PollLayoutType, try_enum From be13145bbd4c22b9a52f4cb2095f2b266479f522 Mon Sep 17 00:00:00 2001 From: Paillat Date: Tue, 5 Aug 2025 21:27:49 +0200 Subject: [PATCH 5/5] Update context.py Signed-off-by: Paillat --- discord/commands/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/commands/context.py b/discord/commands/context.py index 9b59afe559..73a6b39a45 100644 --- a/discord/commands/context.py +++ b/discord/commands/context.py @@ -265,7 +265,7 @@ def unselected_options(self) -> list[Option] | None: return self.command.options # type: ignore return None - @cached_property + @property def attachment_size_limit(self) -> int: """Returns the attachment size limit associated with this context's interaction. Shorthand for :attr:`.Interaction.attachment_size_limit`.