Browse Source

Merge branch 'master' into feat/share-client-theme

pull/10428/head
Soheab 1 month ago
committed by GitHub
parent
commit
bc396c54ec
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/lint.yml
  2. 27
      discord/app_commands/commands.py
  3. 4
      discord/app_commands/transformers.py
  4. 8
      discord/app_commands/tree.py
  5. 4
      discord/client.py
  6. 9
      discord/ext/commands/bot.py
  7. 6
      discord/ext/commands/cog.py
  8. 14
      discord/ext/commands/core.py
  9. 10
      discord/ext/tasks/__init__.py
  10. 5
      discord/guild.py
  11. 7
      discord/member.py
  12. 4
      discord/ui/action_row.py
  13. 4
      discord/ui/button.py
  14. 15
      discord/ui/select.py
  15. 9
      discord/utils.py
  16. 4
      docs/_static/style.css
  17. 4
      docs/api.rst
  18. 6
      docs/ext/commands/commands.rst
  19. 2
      docs/ext/commands/extensions.rst
  20. 2
      docs/intents.rst
  21. 8
      docs/interactions/api.rst
  22. 5
      docs/version_guarantees.rst
  23. 1
      pyproject.toml
  24. 14
      tests/test_permissions_all.py

2
.github/workflows/lint.yml

@ -45,4 +45,4 @@ jobs:
- name: Run ruff - name: Run ruff
if: ${{ always() && steps.install-deps.outcome == 'success' }} if: ${{ always() && steps.install-deps.outcome == 'success' }}
run: | run: |
ruff format --check discord examples ruff format --check

27
discord/app_commands/commands.py

@ -58,7 +58,16 @@ from ..message import Message
from ..user import User from ..user import User
from ..member import Member from ..member import Member
from ..permissions import Permissions from ..permissions import Permissions
from ..utils import resolve_annotation, MISSING, is_inside_class, maybe_coroutine, async_all, _shorten, _to_kebab_case from ..utils import (
resolve_annotation,
MISSING,
is_inside_class,
maybe_coroutine,
async_all,
_iscoroutinefunction,
_shorten,
_to_kebab_case,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import ParamSpec, Concatenate, Unpack from typing_extensions import ParamSpec, Concatenate, Unpack
@ -346,7 +355,7 @@ def _populate_autocomplete(params: Dict[str, CommandParameter], autocomplete: Di
if callback is MISSING: if callback is MISSING:
continue continue
if not inspect.iscoroutinefunction(callback): if not _iscoroutinefunction(callback):
raise TypeError('autocomplete callback must be a coroutine function') raise TypeError('autocomplete callback must be a coroutine function')
if param.type not in (AppCommandOptionType.string, AppCommandOptionType.number, AppCommandOptionType.integer): if param.type not in (AppCommandOptionType.string, AppCommandOptionType.number, AppCommandOptionType.integer):
@ -1037,7 +1046,7 @@ class Command(Generic[GroupT, P, T]):
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The error handler must be a coroutine.') raise TypeError('The error handler must be a coroutine.')
self.on_error = coro self.on_error = coro
@ -1098,7 +1107,7 @@ class Command(Generic[GroupT, P, T]):
""" """
def decorator(coro: AutocompleteCallback[GroupT, ChoiceT]) -> AutocompleteCallback[GroupT, ChoiceT]: def decorator(coro: AutocompleteCallback[GroupT, ChoiceT]) -> AutocompleteCallback[GroupT, ChoiceT]:
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The autocomplete callback must be a coroutine function.') raise TypeError('The autocomplete callback must be a coroutine function.')
try: try:
@ -1347,7 +1356,7 @@ class ContextMenu:
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The error handler must be a coroutine.') raise TypeError('The error handler must be a coroutine.')
self.on_error = coro self.on_error = coro
@ -1840,7 +1849,7 @@ class Group:
The coroutine passed is not actually a coroutine, or is an invalid coroutine. The coroutine passed is not actually a coroutine, or is an invalid coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The error handler must be a coroutine.') raise TypeError('The error handler must be a coroutine.')
params = inspect.signature(coro).parameters params = inspect.signature(coro).parameters
@ -1990,7 +1999,7 @@ class Group:
""" """
def decorator(func: CommandCallback[GroupT, P, T]) -> Command[GroupT, P, T]: def decorator(func: CommandCallback[GroupT, P, T]) -> Command[GroupT, P, T]:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('command function must be a coroutine function') raise TypeError('command function must be a coroutine function')
if description is MISSING: if description is MISSING:
@ -2051,7 +2060,7 @@ def command(
""" """
def decorator(func: CommandCallback[GroupT, P, T]) -> Command[GroupT, P, T]: def decorator(func: CommandCallback[GroupT, P, T]) -> Command[GroupT, P, T]:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('command function must be a coroutine function') raise TypeError('command function must be a coroutine function')
if description is MISSING: if description is MISSING:
@ -2123,7 +2132,7 @@ def context_menu(
""" """
def decorator(func: ContextMenuCallback) -> ContextMenu: def decorator(func: ContextMenuCallback) -> ContextMenu:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('context menu function must be a coroutine function') raise TypeError('context menu function must be a coroutine function')
actual_name = func.__name__.title() if name is MISSING else name actual_name = func.__name__.title() if name is MISSING else name

4
discord/app_commands/transformers.py

@ -53,7 +53,7 @@ from ..channel import StageChannel, VoiceChannel, TextChannel, CategoryChannel,
from ..abc import GuildChannel from ..abc import GuildChannel
from ..threads import Thread from ..threads import Thread
from ..enums import Enum as InternalEnum, AppCommandOptionType, ChannelType, Locale from ..enums import Enum as InternalEnum, AppCommandOptionType, ChannelType, Locale
from ..utils import MISSING, maybe_coroutine, _human_join, TIMESTAMP_PATTERN from ..utils import MISSING, maybe_coroutine, _human_join, _iscoroutinefunction, TIMESTAMP_PATTERN
from ..user import User from ..user import User
from ..role import Role from ..role import Role
from ..member import Member from ..member import Member
@ -814,7 +814,7 @@ def get_supported_annotation(
params = inspect.signature(transform_classmethod.__func__).parameters params = inspect.signature(transform_classmethod.__func__).parameters
if len(params) != 3: if len(params) != 3:
raise TypeError('Inline transformer with transform classmethod requires 3 parameters') raise TypeError('Inline transformer with transform classmethod requires 3 parameters')
if not inspect.iscoroutinefunction(transform_classmethod.__func__): if not _iscoroutinefunction(transform_classmethod.__func__):
raise TypeError('Inline transformer with transform classmethod must be a coroutine') raise TypeError('Inline transformer with transform classmethod must be a coroutine')
return (InlineTransformer(annotation), MISSING, False) return (InlineTransformer(annotation), MISSING, False)

8
discord/app_commands/tree.py

@ -62,7 +62,7 @@ from .installs import AppCommandContext, AppInstallationType
from .translator import Translator, locale_str from .translator import Translator, locale_str
from ..errors import ClientException, HTTPException from ..errors import ClientException, HTTPException
from ..enums import AppCommandType, InteractionType from ..enums import AppCommandType, InteractionType
from ..utils import MISSING, _get_as_snowflake, _is_submodule, _shorten from ..utils import MISSING, _get_as_snowflake, _iscoroutinefunction, _is_submodule, _shorten
from .._types import ClientT from .._types import ClientT
@ -839,7 +839,7 @@ class CommandTree(Generic[ClientT]):
not match the signature. not match the signature.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The error handler must be a coroutine.') raise TypeError('The error handler must be a coroutine.')
params = inspect.signature(coro).parameters params = inspect.signature(coro).parameters
@ -908,7 +908,7 @@ class CommandTree(Generic[ClientT]):
""" """
def decorator(func: CommandCallback[Group, P, T]) -> Command[Group, P, T]: def decorator(func: CommandCallback[Group, P, T]) -> Command[Group, P, T]:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('command function must be a coroutine function') raise TypeError('command function must be a coroutine function')
if description is MISSING: if description is MISSING:
@ -1005,7 +1005,7 @@ class CommandTree(Generic[ClientT]):
""" """
def decorator(func: ContextMenuCallback) -> ContextMenu: def decorator(func: ContextMenuCallback) -> ContextMenu:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('context menu function must be a coroutine function') raise TypeError('context menu function must be a coroutine function')
actual_name = func.__name__.title() if name is MISSING else name actual_name = func.__name__.title() if name is MISSING else name

4
discord/client.py

@ -68,7 +68,7 @@ from .voice_client import VoiceClient
from .http import HTTPClient from .http import HTTPClient
from .state import ConnectionState from .state import ConnectionState
from . import utils from . import utils
from .utils import MISSING, time_snowflake, deprecated from .utils import MISSING, time_snowflake, deprecated, _iscoroutinefunction
from .object import Object from .object import Object
from .backoff import ExponentialBackoff from .backoff import ExponentialBackoff
from .webhook import Webhook from .webhook import Webhook
@ -2098,7 +2098,7 @@ class Client:
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('event registered must be a coroutine function') raise TypeError('event registered must be a coroutine function')
setattr(self, coro.__name__, coro) setattr(self, coro.__name__, coro)

9
discord/ext/commands/bot.py

@ -25,7 +25,6 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
import asyncio
import collections import collections
import collections.abc import collections.abc
import inspect import inspect
@ -53,7 +52,7 @@ from typing import (
import discord import discord
from discord import app_commands from discord import app_commands
from discord.app_commands.tree import _retrieve_guild_ids from discord.app_commands.tree import _retrieve_guild_ids
from discord.utils import MISSING, _is_submodule from discord.utils import MISSING, _iscoroutinefunction, _is_submodule
from .core import GroupMixin from .core import GroupMixin
from .view import StringView from .view import StringView
@ -581,7 +580,7 @@ class BotBase(GroupMixin[None]):
TypeError TypeError
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The pre-invoke hook must be a coroutine.') raise TypeError('The pre-invoke hook must be a coroutine.')
self._before_invoke = coro self._before_invoke = coro
@ -618,7 +617,7 @@ class BotBase(GroupMixin[None]):
TypeError TypeError
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError('The post-invoke hook must be a coroutine.') raise TypeError('The post-invoke hook must be a coroutine.')
self._after_invoke = coro self._after_invoke = coro
@ -654,7 +653,7 @@ class BotBase(GroupMixin[None]):
""" """
name = func.__name__ if name is MISSING else name name = func.__name__ if name is MISSING else name
if not asyncio.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('Listeners must be coroutines') raise TypeError('Listeners must be coroutines')
if name in self.extra_events: if name in self.extra_events:

6
discord/ext/commands/cog.py

@ -28,7 +28,7 @@ import inspect
import discord import discord
import logging import logging
from discord import app_commands from discord import app_commands
from discord.utils import maybe_coroutine, _to_kebab_case from discord.utils import maybe_coroutine, _iscoroutinefunction, _to_kebab_case
from typing import ( from typing import (
Any, Any,
@ -233,7 +233,7 @@ class CogMeta(type):
if elem.startswith(('cog_', 'bot_')): if elem.startswith(('cog_', 'bot_')):
raise TypeError(no_bot_cog.format(base, elem)) raise TypeError(no_bot_cog.format(base, elem))
cog_app_commands[elem] = value cog_app_commands[elem] = value
elif inspect.iscoroutinefunction(value): elif _iscoroutinefunction(value):
try: try:
getattr(value, '__cog_listener__') getattr(value, '__cog_listener__')
except AttributeError: except AttributeError:
@ -522,7 +522,7 @@ class Cog(metaclass=CogMeta):
actual = func actual = func
if isinstance(actual, staticmethod): if isinstance(actual, staticmethod):
actual = actual.__func__ actual = actual.__func__
if not inspect.iscoroutinefunction(actual): if not _iscoroutinefunction(actual):
raise TypeError('Listener function must be a coroutine function.') raise TypeError('Listener function must be a coroutine function.')
actual.__cog_listener__ = True actual.__cog_listener__ = True
to_assign = name or actual.__name__ to_assign = name or actual.__name__

14
discord/ext/commands/core.py

@ -427,7 +427,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
/, /,
**kwargs: Unpack[_CommandKwargs], **kwargs: Unpack[_CommandKwargs],
) -> None: ) -> None:
if not asyncio.iscoroutinefunction(func): if not discord.utils._iscoroutinefunction(func):
raise TypeError('Callback must be a coroutine.') raise TypeError('Callback must be a coroutine.')
name = kwargs.get('name') or func.__name__ name = kwargs.get('name') or func.__name__
@ -1102,7 +1102,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not discord.utils._iscoroutinefunction(coro):
raise TypeError('The error handler must be a coroutine.') raise TypeError('The error handler must be a coroutine.')
self.on_error: Error[CogT, Any] = coro self.on_error: Error[CogT, Any] = coro
@ -1140,7 +1140,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
TypeError TypeError
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not discord.utils._iscoroutinefunction(coro):
raise TypeError('The pre-invoke hook must be a coroutine.') raise TypeError('The pre-invoke hook must be a coroutine.')
self._before_invoke = coro self._before_invoke = coro
@ -1171,7 +1171,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
TypeError TypeError
The coroutine passed is not actually a coroutine. The coroutine passed is not actually a coroutine.
""" """
if not asyncio.iscoroutinefunction(coro): if not discord.utils._iscoroutinefunction(coro):
raise TypeError('The post-invoke hook must be a coroutine.') raise TypeError('The post-invoke hook must be a coroutine.')
self._after_invoke = coro self._after_invoke = coro
@ -1945,7 +1945,7 @@ def check(predicate: UserCheck[ContextT], /) -> Check[ContextT]:
return func return func
if inspect.iscoroutinefunction(predicate): if discord.utils._iscoroutinefunction(predicate):
decorator.predicate = predicate decorator.predicate = predicate
else: else:
@ -2369,7 +2369,7 @@ def guild_only() -> Check[Any]:
return func return func
if inspect.iscoroutinefunction(predicate): if discord.utils._iscoroutinefunction(predicate):
decorator.predicate = predicate decorator.predicate = predicate
else: else:
@ -2444,7 +2444,7 @@ def is_nsfw() -> Check[Any]:
return func return func
if inspect.iscoroutinefunction(predicate): if discord.utils._iscoroutinefunction(predicate):
decorator.predicate = predicate decorator.predicate = predicate
else: else:

10
discord/ext/tasks/__init__.py

@ -46,7 +46,7 @@ import inspect
from collections.abc import Sequence from collections.abc import Sequence
from discord.backoff import ExponentialBackoff from discord.backoff import ExponentialBackoff
from discord.utils import MISSING from discord.utils import MISSING, _iscoroutinefunction
_log = logging.getLogger(__name__) _log = logging.getLogger(__name__)
@ -182,7 +182,7 @@ class Loop(Generic[LF]):
self._last_iteration: datetime.datetime = MISSING self._last_iteration: datetime.datetime = MISSING
self._next_iteration = None self._next_iteration = None
if not inspect.iscoroutinefunction(self.coro): if not _iscoroutinefunction(self.coro):
raise TypeError(f'Expected coroutine function, not {type(self.coro).__name__!r}.') raise TypeError(f'Expected coroutine function, not {type(self.coro).__name__!r}.')
async def _call_loop_function(self, name: str, *args: Any, **kwargs: Any) -> None: async def _call_loop_function(self, name: str, *args: Any, **kwargs: Any) -> None:
@ -574,7 +574,7 @@ class Loop(Generic[LF]):
The function was not a coroutine. The function was not a coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.') raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.')
self._before_loop = coro self._before_loop = coro
@ -602,7 +602,7 @@ class Loop(Generic[LF]):
The function was not a coroutine. The function was not a coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.') raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.')
self._after_loop = coro self._after_loop = coro
@ -632,7 +632,7 @@ class Loop(Generic[LF]):
TypeError TypeError
The function was not a coroutine. The function was not a coroutine.
""" """
if not inspect.iscoroutinefunction(coro): if not _iscoroutinefunction(coro):
raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.') raise TypeError(f'Expected coroutine function, received {coro.__class__.__name__}.')
self._error = coro # type: ignore self._error = coro # type: ignore

5
discord/guild.py

@ -2027,7 +2027,7 @@ class Guild(Hashable):
widget_channel: Optional[Snowflake] = MISSING, widget_channel: Optional[Snowflake] = MISSING,
mfa_level: MFALevel = MISSING, mfa_level: MFALevel = MISSING,
raid_alerts_disabled: bool = MISSING, raid_alerts_disabled: bool = MISSING,
safety_alerts_channel: TextChannel = MISSING, safety_alerts_channel: Optional[TextChannel] = MISSING,
invites_disabled_until: datetime.datetime = MISSING, invites_disabled_until: datetime.datetime = MISSING,
dms_disabled_until: datetime.datetime = MISSING, dms_disabled_until: datetime.datetime = MISSING,
) -> Guild: ) -> Guild:
@ -2285,8 +2285,7 @@ class Guild(Hashable):
raise TypeError( raise TypeError(
f'safety_alerts_channel must be of type TextChannel not {safety_alerts_channel.__class__.__name__}' f'safety_alerts_channel must be of type TextChannel not {safety_alerts_channel.__class__.__name__}'
) )
fields['safety_alerts_channel_id'] = safety_alerts_channel.id
fields['safety_alerts_channel_id'] = safety_alerts_channel.id
if owner is not MISSING: if owner is not MISSING:
if self.owner_id != self._state.self_id: if self.owner_id != self._state.self_id:

7
discord/member.py

@ -25,7 +25,6 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
import datetime import datetime
import inspect
import itertools import itertools
from operator import attrgetter from operator import attrgetter
from typing import Any, Awaitable, Callable, Collection, Dict, List, Optional, TYPE_CHECKING, Tuple, TypeVar, Union from typing import Any, Awaitable, Callable, Collection, Dict, List, Optional, TYPE_CHECKING, Tuple, TypeVar, Union
@ -190,7 +189,7 @@ def flatten_user(cls: T) -> T:
# probably a member function by now # probably a member function by now
def generate_function(x): def generate_function(x):
# We want sphinx to properly show coroutine functions as coroutines # We want sphinx to properly show coroutine functions as coroutines
if inspect.iscoroutinefunction(value): if utils._iscoroutinefunction(value):
async def general(self, *args, **kwargs): # type: ignore async def general(self, *args, **kwargs): # type: ignore
return await getattr(self._user, x)(*args, **kwargs) return await getattr(self._user, x)(*args, **kwargs)
@ -977,7 +976,7 @@ class Member(discord.abc.Messageable, _UserTag):
await http.edit_my_voice_state(guild_id, voice_state_payload) await http.edit_my_voice_state(guild_id, voice_state_payload)
else: else:
if not suppress: if not suppress:
voice_state_payload['request_to_speak_timestamp'] = datetime.datetime.utcnow().isoformat() voice_state_payload['request_to_speak_timestamp'] = utils.utcnow().isoformat()
await http.edit_voice_state(guild_id, self.id, voice_state_payload) await http.edit_voice_state(guild_id, self.id, voice_state_payload)
if voice_channel is not MISSING: if voice_channel is not MISSING:
@ -1038,7 +1037,7 @@ class Member(discord.abc.Messageable, _UserTag):
payload = { payload = {
'channel_id': self.voice.channel.id, 'channel_id': self.voice.channel.id,
'request_to_speak_timestamp': datetime.datetime.utcnow().isoformat(), 'request_to_speak_timestamp': utils.utcnow().isoformat(),
} }
if self._state.self_id != self.id: if self._state.self_id != self.id:

4
discord/ui/action_row.py

@ -274,7 +274,7 @@ class ActionRow(Item[V]):
item._update_view(self.view) item._update_view(self.view)
item._parent = self item._parent = self
self._weight += 1 self._weight += item.width
self._children.append(item) self._children.append(item)
return self return self
@ -298,7 +298,7 @@ class ActionRow(Item[V]):
else: else:
if self._view: if self._view:
self._view._add_count(-1) self._view._add_count(-1)
self._weight -= 1 self._weight -= item.width
return self return self

4
discord/ui/button.py

@ -26,7 +26,6 @@ from __future__ import annotations
import copy import copy
from typing import Callable, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union from typing import Callable, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Union
import inspect
import os import os
@ -34,6 +33,7 @@ from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCall
from ..enums import ButtonStyle, ComponentType from ..enums import ButtonStyle, ComponentType
from ..partial_emoji import PartialEmoji, _EmojiTag from ..partial_emoji import PartialEmoji, _EmojiTag
from ..components import Button as ButtonComponent from ..components import Button as ButtonComponent
from ..utils import _iscoroutinefunction
__all__ = ( __all__ = (
'Button', 'Button',
@ -370,7 +370,7 @@ def button(
""" """
def decorator(func: ItemCallbackType[S, Button[V]]) -> ItemCallbackType[S, Button[V]]: def decorator(func: ItemCallbackType[S, Button[V]]) -> ItemCallbackType[S, Button[V]]:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('button function must be a coroutine function') raise TypeError('button function must be a coroutine function')
func.__discord_ui_model_type__ = Button func.__discord_ui_model_type__ = Button

15
discord/ui/select.py

@ -40,14 +40,13 @@ from typing import (
) )
from contextvars import ContextVar from contextvars import ContextVar
import copy import copy
import inspect
import os import os
from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback from .item import Item, ContainedItemCallbackType as ItemCallbackType, _ItemCallback
from ..enums import ChannelType, ComponentType, SelectDefaultValueType from ..enums import ChannelType, ComponentType, SelectDefaultValueType
from ..partial_emoji import PartialEmoji from ..partial_emoji import PartialEmoji
from ..emoji import Emoji from ..emoji import Emoji
from ..utils import MISSING, _human_join from ..utils import MISSING, _human_join, _iscoroutinefunction
from ..components import ( from ..components import (
SelectOption, SelectOption,
SelectMenu, SelectMenu,
@ -241,7 +240,7 @@ class BaseSelect(Item[V]):
min_values: Optional[int] = None, min_values: Optional[int] = None,
max_values: Optional[int] = None, max_values: Optional[int] = None,
disabled: bool = False, disabled: bool = False,
required: bool = False, required: bool = True,
options: List[SelectOption] = MISSING, options: List[SelectOption] = MISSING,
channel_types: List[ChannelType] = MISSING, channel_types: List[ChannelType] = MISSING,
default_values: Sequence[SelectDefaultValue] = MISSING, default_values: Sequence[SelectDefaultValue] = MISSING,
@ -640,7 +639,7 @@ class UserSelect(BaseSelect[V]):
min_values: int = 1, min_values: int = 1,
max_values: int = 1, max_values: int = 1,
disabled: bool = False, disabled: bool = False,
required: bool = False, required: bool = True,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None, id: Optional[int] = None,
@ -748,7 +747,7 @@ class RoleSelect(BaseSelect[V]):
min_values: int = 1, min_values: int = 1,
max_values: int = 1, max_values: int = 1,
disabled: bool = False, disabled: bool = False,
required: bool = False, required: bool = True,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None, id: Optional[int] = None,
@ -852,7 +851,7 @@ class MentionableSelect(BaseSelect[V]):
min_values: int = 1, min_values: int = 1,
max_values: int = 1, max_values: int = 1,
disabled: bool = False, disabled: bool = False,
required: bool = False, required: bool = True,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None, id: Optional[int] = None,
@ -966,7 +965,7 @@ class ChannelSelect(BaseSelect[V]):
min_values: int = 1, min_values: int = 1,
max_values: int = 1, max_values: int = 1,
disabled: bool = False, disabled: bool = False,
required: bool = False, required: bool = True,
row: Optional[int] = None, row: Optional[int] = None,
default_values: Sequence[ValidDefaultValues] = MISSING, default_values: Sequence[ValidDefaultValues] = MISSING,
id: Optional[int] = None, id: Optional[int] = None,
@ -1209,7 +1208,7 @@ def select(
""" """
def decorator(func: ItemCallbackType[S, BaseSelectT]) -> ItemCallbackType[S, BaseSelectT]: def decorator(func: ItemCallbackType[S, BaseSelectT]) -> ItemCallbackType[S, BaseSelectT]:
if not inspect.iscoroutinefunction(func): if not _iscoroutinefunction(func):
raise TypeError('select function must be a coroutine function') raise TypeError('select function must be a coroutine function')
callback_cls = getattr(cls, '__origin__', cls) callback_cls = getattr(cls, '__origin__', cls)
if not issubclass(callback_cls, BaseSelect): if not issubclass(callback_cls, BaseSelect):

9
discord/utils.py

@ -26,6 +26,7 @@ from __future__ import annotations
import array import array
import asyncio import asyncio
import inspect
from textwrap import TextWrapper from textwrap import TextWrapper
from typing import ( from typing import (
Any, Any,
@ -1542,3 +1543,11 @@ class _RawReprMixin:
def __repr__(self) -> str: def __repr__(self) -> str:
value = ' '.join(f'{attr}={getattr(self, attr)!r}' for attr in self.__slots__) value = ' '.join(f'{attr}={getattr(self, attr)!r}' for attr in self.__slots__)
return f'<{self.__class__.__name__} {value}>' return f'<{self.__class__.__name__} {value}>'
# `inspect.iscoroutinefunction()` only became equivalent to (now deprecated) `asyncio.iscoroutinefunction()` in Python 3.12
# https://github.com/python/cpython/issues/122858#issuecomment-2466239748
if sys.version_info >= (3, 12):
_iscoroutinefunction = inspect.iscoroutinefunction
else:
_iscoroutinefunction = asyncio.iscoroutinefunction

4
docs/_static/style.css

@ -387,7 +387,7 @@ aside {
background-color: var(--mobile-nav-background); background-color: var(--mobile-nav-background);
color: var(--mobile-nav-text); color: var(--mobile-nav-text);
z-index: 2; z-index: 2;
max-height: 100vh; max-height: 100dvh;
overflow-y: auto; overflow-y: auto;
overscroll-behavior-y: contain; overscroll-behavior-y: contain;
} }
@ -1285,7 +1285,7 @@ div.code-block-caption {
display: inline-block; display: inline-block;
position: sticky; position: sticky;
top: 1em; top: 1em;
max-height: calc(100vh - 2em); max-height: calc(100dvh - 2em);
max-width: 100%; max-width: 100%;
overflow-y: auto; overflow-y: auto;
margin: 1em; margin: 1em;

4
docs/api.rst

@ -2846,7 +2846,7 @@ of :class:`enum.Enum`.
.. attribute:: scheduled_event_update .. attribute:: scheduled_event_update
A scheduled event was created. A scheduled event was updated.
When this is the action, the type of :attr:`~AuditLogEntry.target` is When this is the action, the type of :attr:`~AuditLogEntry.target` is
the :class:`ScheduledEvent` or :class:`Object` with the ID of the event the :class:`ScheduledEvent` or :class:`Object` with the ID of the event
@ -2866,7 +2866,7 @@ of :class:`enum.Enum`.
.. attribute:: scheduled_event_delete .. attribute:: scheduled_event_delete
A scheduled event was created. A scheduled event was deleted.
When this is the action, the type of :attr:`~AuditLogEntry.target` is When this is the action, the type of :attr:`~AuditLogEntry.target` is
the :class:`ScheduledEvent` or :class:`Object` with the ID of the event the :class:`ScheduledEvent` or :class:`Object` with the ID of the event

6
docs/ext/commands/commands.rst

@ -1212,7 +1212,7 @@ This allows you to define a command as both slash and text command without writi
both counterparts. both counterparts.
In order to define a hybrid command, The command callback should be decorated with In order to define a hybrid command, the command callback should be decorated with
:meth:`.Bot.hybrid_command` decorator. :meth:`.Bot.hybrid_command` decorator.
.. code-block:: python3 .. code-block:: python3
@ -1264,11 +1264,11 @@ Apart from that, all other features such as converters, checks, autocomplete, fl
are supported on hybrid commands. Note that due to a design constraint, decorators related to application commands are supported on hybrid commands. Note that due to a design constraint, decorators related to application commands
such as :func:`discord.app_commands.autocomplete` should be placed below the :func:`~ext.commands.hybrid_command` decorator. such as :func:`discord.app_commands.autocomplete` should be placed below the :func:`~ext.commands.hybrid_command` decorator.
For convenience and ease in writing code, The :class:`~ext.commands.Context` class implements For convenience and ease in writing code, the :class:`~ext.commands.Context` class implements
some behavioural changes for various methods and attributes: some behavioural changes for various methods and attributes:
- :attr:`.Context.interaction` can be used to retrieve the slash command interaction. - :attr:`.Context.interaction` can be used to retrieve the slash command interaction.
- Since interaction can only be responded to once, The :meth:`.Context.send` automatically - Since interaction can only be responded to once, the :meth:`.Context.send` automatically
determines whether to send an interaction response or a followup response. determines whether to send an interaction response or a followup response.
- :meth:`.Context.defer` defers the interaction response for slash commands but shows typing - :meth:`.Context.defer` defers the interaction response for slash commands but shows typing
indicator for text commands. indicator for text commands.

2
docs/ext/commands/extensions.rst

@ -10,7 +10,7 @@ There comes a time in the bot development when you want to extend the bot functi
Primer Primer
-------- --------
An extension at its core is a python file with an entry point called ``setup``. This setup function must be a Python coroutine. It takes a single parameter -- the :class:`~.commands.Bot` that loads the extension. An extension at its core is a Python file with an entry point called ``setup``. This setup function must be a Python coroutine. It takes a single parameter -- the :class:`~.commands.Bot` that loads the extension.
An example extension looks like this: An example extension looks like this:

2
docs/intents.rst

@ -153,7 +153,7 @@ If the cache is disabled or you disable chunking guilds at startup, we might sti
- :meth:`Guild.fetch_member` - :meth:`Guild.fetch_member`
- Used to fetch a member by ID through the HTTP API. - Used to fetch a member by ID through the HTTP API.
- :meth:`Guild.fetch_members` - :meth:`Guild.fetch_members`
- used to fetch a large number of members through the HTTP API. - Used to fetch a large number of members through the HTTP API.
It should be noted that the gateway has a strict rate limit of 120 requests per 60 seconds. It should be noted that the gateway has a strict rate limit of 120 requests per 60 seconds.

8
docs/interactions/api.rst

@ -533,10 +533,10 @@ Enumerations
.. attribute:: file_upload .. attribute:: file_upload
Represents a file upload component, usually in a modal. Represents a file upload component, usually in a modal.
.. versionadded:: 2.7
.. versionadded:: 2.7
.. attribute:: radio_group .. attribute:: radio_group
Represents a radio group component. Represents a radio group component.
@ -634,7 +634,7 @@ Enumerations
A string parameter. A string parameter.
.. attribute:: integer .. attribute:: integer
A integer parameter. An integer parameter.
.. attribute:: boolean .. attribute:: boolean
A boolean parameter. A boolean parameter.

5
docs/version_guarantees.rst

@ -24,9 +24,8 @@ Examples of Non-Breaking Changes
- Adding or removing private underscored attributes. - Adding or removing private underscored attributes.
- Adding an element into the ``__slots__`` of a data class. - Adding an element into the ``__slots__`` of a data class.
- Changing the behaviour of a function to fix a bug. - Changing the behaviour of a function to fix a bug.
- Changes in the typing behaviour of the library - Changes in the typing behaviour of the library.
- Changes in the calling convention of functions that are primarily meant as callbacks - Changes in the calling convention of functions that are primarily meant as callbacks.
- Changes in the documentation. - Changes in the documentation.
- Modifying the internal HTTP handling. - Modifying the internal HTTP handling.
- Upgrading the dependencies to a new version, major or otherwise. - Upgrading the dependencies to a new version, major or otherwise.

1
pyproject.toml

@ -91,6 +91,7 @@ include-package-data = true
[tool.ruff] [tool.ruff]
line-length = 125 line-length = 125
extend-exclude = ["docs", "tests"]
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
combine-as-imports = true combine-as-imports = true

14
tests/test_permissions_all.py

@ -1,7 +1,7 @@
import discord import discord
from functools import reduce from functools import reduce
from operator import or_ from operator import or_
def test_permissions_all(): def test_permissions_all():
assert discord.Permissions.all().value == reduce(or_, discord.Permissions.VALID_FLAGS.values()) assert discord.Permissions.all().value == reduce(or_, discord.Permissions.VALID_FLAGS.values())

Loading…
Cancel
Save