diff --git a/CHANGELOG.md b/CHANGELOG.md index 02431551e5..7bc8542dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Changed +- Migrated away from `utils.deprecated` in favor of `warnings.deprecated` for + deprecation warnings. + ([#3042](https://github.com/Pycord-Development/pycord/pull/3042)) - Updated `Role.is_assignable()` to also check whether the bot has the `MANAGE_ROLES` permission. ([#3048](https://github.com/Pycord-Development/pycord/pull/3048)) diff --git a/discord/_version.py b/discord/_version.py index ba68799dfe..ddc7a10fd7 100644 --- a/discord/_version.py +++ b/discord/_version.py @@ -30,14 +30,12 @@ import warnings from importlib.metadata import PackageNotFoundError, version -from typing_extensions import TypedDict +from typing_extensions import TypedDict, deprecated __all__ = ("__version__", "VersionInfo", "version_info") from typing import Literal, NamedTuple -from .utils import deprecated - try: __version__ = version("py-cord") except PackageNotFoundError: @@ -84,27 +82,37 @@ def advanced(self, value: object) -> None: _advanced = value @property - @deprecated("releaselevel", "2.4") + @deprecated( + "VersionInfo.release_level is deprecated since version 2.4, consider using releaselevel instead." + ) def release_level(self) -> Literal["alpha", "beta", "candidate", "final"]: return self.releaselevel @property - @deprecated('.advanced["serial"]', "2.4") + @deprecated( + 'VersionInfo.serial is deprecated since version 2.4, consider using .advanced["serial"] instead.' + ) def serial(self) -> int: return self.advanced["serial"] @property - @deprecated('.advanced["build"]', "2.4") + @deprecated( + 'VersionInfo.build is deprecated since version 2.4, consider using .advanced["build"] instead.' + ) def build(self) -> int | None: return self.advanced["build"] @property - @deprecated('.advanced["commit"]', "2.4") + @deprecated( + 'VersionInfo.commit is deprecated since version 2.4, consider using .advanced["commit"] instead.' + ) def commit(self) -> str | None: return self.advanced["commit"] @property - @deprecated('.advanced["date"]', "2.4") + @deprecated( + 'VersionInfo.date is deprecated since version 2.4, consider using .advanced["date"] instead.' + ) def date(self) -> datetime.date | None: return self.advanced["date"] diff --git a/discord/channel.py b/discord/channel.py index 76d6f6e085..7722aecdeb 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -38,6 +38,8 @@ overload, ) +from typing_extensions import deprecated + import discord.abc from . import utils @@ -3118,10 +3120,8 @@ async def create_forum_channel(self, name: str, **options: Any) -> ForumChannel: """ return await self.guild.create_forum_channel(name, category=self, **options) - @utils.deprecated( - since="2.7", - removed="3.0", - reference="NSFW categories are not available in the Discord API.", + @deprecated( + "CategoryChannel.is_nsfw is deprecated since version 2.7 and will be removed in version 3.0. NSFW categories are not available in the Discord API." ) def is_nsfw(self) -> bool: return False @@ -3129,10 +3129,8 @@ def is_nsfw(self) -> bool: # TODO: Remove in 3.0 @property - @utils.deprecated( - since="2.7", - removed="3.0", - reference="NSFW categories are not available in the Discord API.", + @deprecated( + "CategoryChannel.nsfw is deprecated since version 2.7 and will be removed in version 3.0. NSFW categories are not available in the Discord API." ) def nsfw(self) -> bool: return False diff --git a/discord/client.py b/discord/client.py index f339cbfe92..ab72e86984 100644 --- a/discord/client.py +++ b/discord/client.py @@ -42,6 +42,7 @@ ) import aiohttp +from typing_extensions import deprecated from . import utils from .activity import ActivityTypes, BaseActivity, create_activity @@ -1181,10 +1182,8 @@ 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", - removed="3.0", + @deprecated( + "Client.get_or_fetch_user is deprecated since version 2.7 and will be removed in version 3.0, consider using Client.get_or_fetch(User, id) instead." ) async def get_or_fetch_user(self, id: int, /) -> User | None: # TODO: Remove in 3.0 """|coro| diff --git a/discord/interactions.py b/discord/interactions.py index 24f995a2bd..4a075d83fd 100644 --- a/discord/interactions.py +++ b/discord/interactions.py @@ -29,6 +29,8 @@ import datetime from typing import TYPE_CHECKING, Any, Coroutine, Union +from typing_extensions import deprecated + from . import utils from .channel import ChannelType, PartialMessageable, _threaded_channel_factory from .enums import ( @@ -222,6 +224,7 @@ def __init__(self, *, data: InteractionPayload, state: ConnectionState): self._session: ClientSession = state.http._HTTPClient__session self._original_response: InteractionMessage | None = None self.callback: InteractionCallback | None = None + self._cs_channel: InteractionChannel | None = MISSING self._from_data(data) def _from_data(self, data: InteractionPayload): @@ -350,14 +353,19 @@ def is_component(self) -> bool: """Indicates whether the interaction is a message component.""" return self.type == InteractionType.component - @utils.cached_slot_property("_cs_channel") - @utils.deprecated("Interaction.channel", "2.7", stacklevel=4) + @property + @deprecated( + "Interaction.cached_channel is deprecated since version 2.7, consider using Interaction.channel instead." + ) def cached_channel(self) -> InteractionChannel | None: """The cached channel from which the interaction was sent. DM channels are not resolved. These are :class:`PartialMessageable` instead. .. deprecated:: 2.7 """ + if self._cs_channel is not MISSING: + return self._cs_channel + r: InteractionChannel | None = None guild = self.guild channel = guild and guild._resolve_channel(self.channel_id) if channel is None: @@ -367,11 +375,11 @@ def cached_channel(self) -> InteractionChannel | None: if self.guild_id is not None else ChannelType.private ) - return PartialMessageable( - state=self._state, id=self.channel_id, type=type - ) - return None - return channel + r = PartialMessageable(state=self._state, id=self.channel_id, type=type) + else: + r = channel + self._cs_channel = r + return r @property def permissions(self) -> Permissions: @@ -489,7 +497,9 @@ async def original_response(self) -> InteractionMessage: self._original_response = message return message - @utils.deprecated("Interaction.original_response", "2.2") + @deprecated( + "Interaction.original_message is deprecated since version 2.2, consider using Interaction.original_response instead." + ) async def original_message(self): """An alias for :meth:`original_response`. @@ -622,7 +632,9 @@ async def edit_original_response( return message - @utils.deprecated("Interaction.edit_original_response", "2.2") + @deprecated( + "Interaction.edit_original_message is deprecated since version 2.2, consider using Interaction.edit_original_response instead." + ) async def edit_original_message(self, **kwargs): """An alias for :meth:`edit_original_response`. @@ -680,7 +692,9 @@ async def delete_original_response(self, *, delay: float | None = None) -> None: else: await func - @utils.deprecated("Interaction.delete_original_response", "2.2") + @deprecated( + "Interaction.delete_original_message is deprecated since version 2.2, consider using Interaction.delete_original_response instead." + ) async def delete_original_message(self, **kwargs): """An alias for :meth:`delete_original_response`. @@ -1374,7 +1388,9 @@ async def send_modal(self, modal: Modal) -> Interaction: self._parent._state.store_modal(modal, self._parent.user.id) return self._parent - @utils.deprecated("a button with type ButtonType.premium", "2.6") + @deprecated( + "InteractionResponse.premium_required is deprecated since version 2.6, consider using a button with type ButtonType.premium instead." + ) async def premium_required(self) -> Interaction: """|coro| diff --git a/discord/role.py b/discord/role.py index 44f4d1d1d5..fee40468e6 100644 --- a/discord/role.py +++ b/discord/role.py @@ -27,7 +27,7 @@ from typing import TYPE_CHECKING, Any, TypeVar -from typing_extensions import Self +from typing_extensions import Self, deprecated from .asset import Asset from .colour import Colour @@ -39,7 +39,6 @@ MISSING, _bytes_to_base64_data, _get_as_snowflake, - deprecated, snowflake_time, warn_deprecated, ) @@ -489,7 +488,9 @@ def permissions(self) -> Permissions: return Permissions(self._permissions) @property - @deprecated("colours.primary", "2.7") + @deprecated( + "Role.colour is deprecated since version 2.7, consider using colours.primary instead." + ) def colour(self) -> Colour: """Returns the role colour. Equivalent to :attr:`colours.primary`. An alias exists under ``color``. @@ -499,7 +500,9 @@ def colour(self) -> Colour: return self.colours.primary @property - @deprecated("colors.primary", "2.7") + @deprecated( + "Role.color is deprecated since version 2.7, consider using colors.primary instead." + ) def color(self) -> Colour: """Returns the role's primary color. Equivalent to :attr:`colors.primary`. An alias exists under ``colour``. diff --git a/discord/utils.py b/discord/utils.py index 37b7c5ffdf..3af3660298 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -94,7 +94,6 @@ __all__ = ( "parse_time", "warn_deprecated", - "deprecated", "oauth_url", "snowflake_time", "time_snowflake", @@ -345,57 +344,6 @@ def warn_deprecated( warnings.warn(message, stacklevel=stacklevel, category=DeprecationWarning) -def deprecated( - instead: str | None = None, - since: str | None = None, - removed: str | None = None, - reference: str | None = None, - stacklevel: int = 3, - *, - use_qualname: bool = True, -) -> Callable[[Callable[[P], T]], Callable[[P], T]]: - """A decorator implementation of :func:`warn_deprecated`. This will automatically call :func:`warn_deprecated` when - the decorated function is called. - - Parameters - ---------- - instead: Optional[:class:`str`] - A recommended alternative to the function. - since: Optional[:class:`str`] - The version in which the function was deprecated. This should be in the format ``major.minor(.patch)``, where - the patch version is optional. - removed: Optional[:class:`str`] - The version in which the function is planned to be removed. This should be in the format - ``major.minor(.patch)``, where the patch version is optional. - reference: Optional[:class:`str`] - A reference that explains the deprecation, typically a URL to a page such as a changelog entry or a GitHub - issue/PR. - stacklevel: :class:`int` - The stacklevel kwarg passed to :func:`warnings.warn`. Defaults to 3. - use_qualname: :class:`bool` - Whether to use the qualified name of the function in the deprecation warning. If ``False``, the short name of - the function will be used instead. For example, __qualname__ will display as ``Client.login`` while __name__ - will display as ``login``. Defaults to ``True``. - """ - - def actual_decorator(func: Callable[[P], T]) -> Callable[[P], T]: - @functools.wraps(func) - def decorated(*args: P.args, **kwargs: P.kwargs) -> T: - warn_deprecated( - name=func.__qualname__ if use_qualname else func.__name__, - instead=instead, - since=since, - removed=removed, - reference=reference, - stacklevel=stacklevel, - ) - return func(*args, **kwargs) - - return decorated - - return actual_decorator - - def oauth_url( client_id: int | str, *,