diff --git a/discord/__main__.py b/discord/__main__.py index 94d344137c..07c00ef91d 100644 --- a/discord/__main__.py +++ b/discord/__main__.py @@ -36,14 +36,13 @@ def show_version() -> None: - version_info = discord.version_info - entries = [ - "- Python v{0.major}.{0.minor}.{0.micro}-{0.releaselevel}".format(sys.version_info), - "- py-cord v{0.major}.{0.minor}.{0.micro}-{0.releaselevel}".format(version_info), - ] + entries = ["- Python v{0.major}.{0.minor}.{0.micro}-{0.releaselevel}".format(sys.version_info)] + version_info = discord.version_info + entries.append("- py-cord v{0.major}.{0.minor}.{0.micro}-{0.releaselevel}".format(version_info)) if version_info.releaselevel != "final": - if pkg := pkg_resources.get_distribution("py-cord"): + pkg = pkg_resources.get_distribution("py-cord") + if pkg: entries.append(f" - py-cord pkg_resources: v{pkg.version}") entries.append(f"- aiohttp v{aiohttp.__version__}") @@ -174,7 +173,7 @@ async def cog_after_invoke(self, ctx): } # NUL (0) and 1-31 are disallowed -_base_table |= ((chr(i), None) for i in range(32)) +_base_table.update((chr(i), None) for i in range(32)) _translation_table = str.maketrans(_base_table) @@ -244,10 +243,11 @@ def newbot(parser, args) -> None: try: with open(str(new_directory / "bot.py"), "w", encoding="utf-8") as fp: - base = "AutoShardedBot" if args.sharded else "Bot" + base = "Bot" if not args.sharded else "AutoShardedBot" fp.write(_bot_template.format(base=base, prefix=args.prefix)) except OSError as exc: parser.error(f"could not create bot file ({exc})") + if not args.no_git: try: with open(str(new_directory / ".gitignore"), "w", encoding="utf-8") as fp: diff --git a/discord/abc.py b/discord/abc.py index a5676af587..44657d26b4 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -422,13 +422,15 @@ async def _edit(self, options: Dict[str, Any], reason: Optional[str]) -> Optiona except KeyError: if parent_id is not _undefined: if lock_permissions: - if category := self.guild.get_channel(parent_id): + category = self.guild.get_channel(parent_id) + if category: options["permission_overwrites"] = [c._asdict() for c in category._overwrites] options["parent_id"] = parent_id elif lock_permissions and self.category_id is not None: # if we're syncing permissions on a pre-existing channel category without changing it # we need to update the permissions to point to the pre-existing category - if category := self.guild.get_channel(self.category_id): + category = self.guild.get_channel(self.category_id) + if category: options["permission_overwrites"] = [c._asdict() for c in category._overwrites] else: await self._move( @@ -489,7 +491,8 @@ def _fill_overwrites(self, data: GuildChannelPayload) -> None: everyone_index = index # do the swap - if tmp := self._overwrites: + tmp = self._overwrites + if tmp: tmp[everyone_index], tmp[0] = tmp[0], tmp[everyone_index] @property @@ -876,8 +879,8 @@ async def set_permissions(self, target, *, overwrite=_undefined, reason=None, ** raise InvalidArgument("No overwrite provided.") try: overwrite = PermissionOverwrite(**permissions) - except (ValueError, TypeError) as e: - raise InvalidArgument("Invalid permissions given to keyword arguments.") from e + except (ValueError, TypeError): + raise InvalidArgument("Invalid permissions given to keyword arguments.") elif len(permissions) > 0: raise InvalidArgument("Cannot mix overwrite and keyword arguments.") @@ -1671,8 +1674,8 @@ def can_send(self, *objects) -> bool: if obj.guild_id == channel.guild.id: continue - except (KeyError, AttributeError) as e: - raise TypeError(f"The object {obj} is of an invalid type.") from e + except (KeyError, AttributeError): + raise TypeError(f"The object {obj} is of an invalid type.") if not getattr(channel.permissions_for(channel.guild.me), permission): return False diff --git a/discord/activity.py b/discord/activity.py index fb6fe74e32..8202d468ae 100644 --- a/discord/activity.py +++ b/discord/activity.py @@ -841,8 +841,10 @@ def create_activity(data: Optional[ActivityPayload]) -> Optional[ActivityTypes]: # we removed the name key from data already return CustomActivity(name=name, **data) # type: ignore elif game_type is ActivityType.streaming: - # the url won't be None here - return Streaming(**data) if "url" in data else Activity(**data) + if "url" in data: + # the url won't be None here + return Streaming(**data) # type: ignore + return Activity(**data) elif game_type is ActivityType.listening and "sync_id" in data and "session_id" in data: return Spotify(**data) return Activity(**data) diff --git a/discord/asset.py b/discord/asset.py index c54ed5b602..829340361a 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -174,10 +174,10 @@ def _from_default_avatar(cls, state, index: int) -> Asset: @classmethod def _from_avatar(cls, state, user_id: int, avatar: str) -> Asset: animated = avatar.startswith("a_") - fmt = "gif" if animated else "png" + format = "gif" if animated else "png" return cls( state, - url=f"{cls.BASE}/avatars/{user_id}/{avatar}.{fmt}?size=1024", + url=f"{cls.BASE}/avatars/{user_id}/{avatar}.{format}?size=1024", key=avatar, animated=animated, ) @@ -185,10 +185,10 @@ def _from_avatar(cls, state, user_id: int, avatar: str) -> Asset: @classmethod def _from_guild_avatar(cls, state, guild_id: int, member_id: int, avatar: str) -> Asset: animated = avatar.startswith("a_") - fmt = "gif" if animated else "png" + format = "gif" if animated else "png" return cls( state, - url=f"{cls.BASE}/guilds/{guild_id}/users/{member_id}/avatars/{avatar}.{fmt}?size=1024", + url=f"{cls.BASE}/guilds/{guild_id}/users/{member_id}/avatars/{avatar}.{format}?size=1024", key=avatar, animated=animated, ) @@ -214,14 +214,14 @@ def _from_cover_image(cls, state, object_id: int, cover_image_hash: str) -> Asse @classmethod def _from_guild_image(cls, state, guild_id: int, image: str, path: str) -> Asset: animated = False - fmt = "png" + format = "png" if path == "banners": animated = image.startswith("a_") - fmt = "gif" if animated else "png" + format = "gif" if animated else "png" return cls( state, - url=f"{cls.BASE}/{path}/{guild_id}/{image}.{fmt}?size=1024", + url=f"{cls.BASE}/{path}/{guild_id}/{image}.{format}?size=1024", key=image, animated=animated, ) @@ -229,10 +229,10 @@ def _from_guild_image(cls, state, guild_id: int, image: str, path: str) -> Asset @classmethod def _from_guild_icon(cls, state, guild_id: int, icon_hash: str) -> Asset: animated = icon_hash.startswith("a_") - fmt = "gif" if animated else "png" + format = "gif" if animated else "png" return cls( state, - url=f"{cls.BASE}/icons/{guild_id}/{icon_hash}.{fmt}?size=1024", + url=f"{cls.BASE}/icons/{guild_id}/{icon_hash}.{format}?size=1024", key=icon_hash, animated=animated, ) @@ -249,10 +249,10 @@ def _from_sticker_banner(cls, state, banner: int) -> Asset: @classmethod def _from_user_banner(cls, state, user_id: int, banner_hash: str) -> Asset: animated = banner_hash.startswith("a_") - fmt = "gif" if animated else "png" + format = "gif" if animated else "png" return cls( state, - url=f"{cls.BASE}/banners/{user_id}/{banner_hash}.{fmt}?size=512", + url=f"{cls.BASE}/banners/{user_id}/{banner_hash}.{format}?size=512", key=banner_hash, animated=animated, ) @@ -344,13 +344,13 @@ def replace( raise InvalidArgument(f"static_format must be one of {VALID_STATIC_FORMATS}") url = url.with_path(f"{path}.{static_format}") - if size == MISSING: + if size is not MISSING: + if not utils.valid_icon_size(size): + raise InvalidArgument("size must be a power of 2 between 16 and 4096") + url = url.with_query(size=size) + else: url = url.with_query(url.raw_query_string) - elif not utils.valid_icon_size(size): - raise InvalidArgument("size must be a power of 2 between 16 and 4096") - else: - url = url.with_query(size=size) url = str(url) return Asset(state=self._state, url=url, key=self._key, animated=self._animated) @@ -430,4 +430,6 @@ def with_static_format(self, format: ValidStaticFormatTypes, /) -> Asset: The new updated asset. """ - return self if self._animated else self.with_format(format) + if self._animated: + return self + return self.with_format(format) diff --git a/discord/audit_logs.py b/discord/audit_logs.py index e5c415dc57..0f1471d06c 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -95,11 +95,15 @@ def _transform_channel(entry: AuditLogEntry, data: Optional[Snowflake]) -> Optio def _transform_member_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Union[Member, User, None]: - return None if data is None else entry._get_member(int(data)) + if data is None: + return None + return entry._get_member(int(data)) def _transform_guild_id(entry: AuditLogEntry, data: Optional[Snowflake]) -> Optional[Guild]: - return None if data is None else entry._state._get_guild(data) + if data is None: + return None + return entry._state._get_guild(data) def _transform_overwrites( diff --git a/discord/bot.py b/discord/bot.py index 0d184e6665..912bc2d510 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -245,7 +245,7 @@ def _check_command(cmd: ApplicationCommand, match: Dict) -> bool: if isinstance(cmd, SlashCommandGroup): if len(cmd.subcommands) != len(match.get("options", [])): return True - for subcommand in cmd.subcommands: + for i, subcommand in enumerate(cmd.subcommands): match_ = next( (data for data in match["options"] if data["name"] == subcommand.name), MISSING, @@ -570,8 +570,8 @@ async def sync_commands( force: bool = False, guild_ids: Optional[List[int]] = None, register_guild_commands: bool = True, - check_guilds: Optional[List[int]] = None, - delete_exiting: bool = True, + check_guilds: Optional[List[int]] = [], + delete_existing: bool = True, ) -> None: """|coro| @@ -615,8 +615,6 @@ async def sync_commands( Whether to delete existing commands that are not in the list of commands to register. Defaults to True. """ - if check_guilds is None: - check_guilds = [] check_guilds = list(set((check_guilds or []) + (self.debug_guilds or []))) if commands is None: @@ -649,12 +647,13 @@ async def sync_commands( global_permissions: List = [] for i in registered_commands: - if cmd := get( + cmd = get( self.pending_application_commands, name=i["name"], guild_ids=None, type=i["type"], - ): + ) + if cmd: cmd.id = i["id"] self._application_commands[cmd.id] = cmd @@ -1195,7 +1194,7 @@ async def my_message(message): pass bot.add_listener(on_ready) bot.add_listener(my_message, 'on_message') """ - name = func.__name__ if name == MISSING else name + name = func.__name__ if name is MISSING else name if not asyncio.iscoroutinefunction(func): raise TypeError("Listeners must be coroutines") @@ -1217,7 +1216,7 @@ def remove_listener(self, func: CoroFunc, name: str = MISSING) -> None: ``func.__name__``. """ - name = func.__name__ if name == MISSING else name + name = func.__name__ if name is MISSING else name if name in self.extra_events: try: diff --git a/discord/channel.py b/discord/channel.py index bb82420976..1af0a53c43 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -563,8 +563,7 @@ async def create_webhook( if avatar is not None: avatar = utils._bytes_to_base64_data(avatar) # type: ignore - data = await self._state.http.create_webhook(self.id, name=name, avatar=avatar, reason=reason) - + data = await self._state.http.create_webhook(self.id, name=str(name), avatar=avatar, reason=reason) return Webhook.from_state(data, state=self._state) async def follow(self, *, destination: TextChannel, reason: Optional[str] = None) -> Webhook: @@ -732,7 +731,7 @@ async def create_thread( name: str, message: Optional[Snowflake] = None, auto_archive_duration: ThreadArchiveDuration = MISSING, - channel_type: Optional[ChannelType] = None, + type: Optional[ChannelType] = None, reason: Optional[str] = None, ) -> Thread: """|coro| @@ -755,7 +754,7 @@ async def create_thread( auto_archive_duration: :class:`int` The duration in minutes before a thread is automatically archived for inactivity. If not provided, the channel's default auto archive duration is used. - channel_type: Optional[:class:`ChannelType`] + type: Optional[:class:`ChannelType`] The type of thread to create. If a ``message`` is passed then this parameter is ignored, as a thread created with a message is always a public thread. By default this creates a private thread if this is ``None``. @@ -775,15 +774,15 @@ async def create_thread( The created thread """ - if channel_type is None: - channel_type = ChannelType.private_thread + if type is None: + type = ChannelType.private_thread if message is None: data = await self._state.http.start_thread_without_message( self.id, name=name, auto_archive_duration=auto_archive_duration or self.default_auto_archive_duration, - type=channel_type.value, + type=type.value, reason=reason, ) else: @@ -1410,8 +1409,7 @@ async def create_webhook( if avatar is not None: avatar = utils._bytes_to_base64_data(avatar) # type: ignore - data = await self._state.http.create_webhook(self.id, name=name, avatar=avatar, reason=reason) - + data = await self._state.http.create_webhook(self.id, name=str(name), avatar=avatar, reason=reason) return Webhook.from_state(data, state=self._state) @property diff --git a/discord/client.py b/discord/client.py index 317836442e..e4c5434689 100644 --- a/discord/client.py +++ b/discord/client.py @@ -283,7 +283,7 @@ def latency(self) -> float: This could be referred to as the Discord WebSocket protocol latency. """ ws = self.ws - return ws.latency if ws else float("nan") + return float("nan") if not ws else ws.latency def is_ws_ratelimited(self) -> bool: """:class:`bool`: Whether the websocket is currently rate limited. @@ -293,7 +293,9 @@ def is_ws_ratelimited(self) -> bool: .. versionadded:: 1.6 """ - return self.ws.is_ratelimited() if self.ws else False + if self.ws: + return self.ws.is_ratelimited() + return False @property def user(self) -> Optional[ClientUser]: @@ -401,7 +403,8 @@ def dispatch(self, event: str, *args: Any, **kwargs: Any) -> None: _log.debug("Dispatching event %s", event) method = f"on_{event}" - if listeners := self._listeners.get(event): + listeners = self._listeners.get(event) + if listeners: removed = [] for i, (future, condition) in enumerate(listeners): if future.cancelled(): @@ -415,7 +418,7 @@ def dispatch(self, event: str, *args: Any, **kwargs: Any) -> None: removed.append(i) else: if result: - if not args: + if len(args) == 0: future.set_result(None) elif len(args) == 1: future.set_result(args[0]) @@ -1354,7 +1357,11 @@ async def create_guild( The guild created. This is not the same guild that is added to cache. """ - icon_base64 = utils._bytes_to_base64_data(icon) if icon != MISSING else None + if icon is not MISSING: + icon_base64 = utils._bytes_to_base64_data(icon) + else: + icon_base64 = None + region_value = str(region) if code: @@ -1691,7 +1698,8 @@ async def create_dm(self, user: Snowflake) -> DMChannel: The channel that was created. """ state = self._connection - if found := state._get_private_channel_by_user(user.id): + found = state._get_private_channel_by_user(user.id) + if found: return found data = await state.http.start_private_message(user.id) diff --git a/discord/cog.py b/discord/cog.py index 53fa973461..21c326440d 100644 --- a/discord/cog.py +++ b/discord/cog.py @@ -220,11 +220,10 @@ def __new__(cls: Type[CogMeta], *args: Any, **kwargs: Any) -> CogMeta: listeners_as_list = [] for listener in listeners.values(): - # I use __name__ instead of just storing the value so I can inject - # the self attribute when the time comes to add them to the bot - listeners_as_list.extend( - (listener_name, listener.__name__) for listener_name in listener.__cog_listener_names__ - ) + for listener_name in listener.__cog_listener_names__: + # I use __name__ instead of just storing the value so I can inject + # the self attribute when the time comes to add them to the bot + listeners_as_list.append((listener_name, listener.__name__)) new_cls.__cog_listeners__ = listeners_as_list @@ -720,9 +719,9 @@ def _load_from_module_spec(self, spec: importlib.machinery.ModuleSpec, key: str) try: setup = getattr(lib, "setup") - except AttributeError as exc: + except AttributeError: del sys.modules[key] - raise errors.NoEntryPointError(key) from exc + raise errors.NoEntryPointError(key) try: setup(self) @@ -737,8 +736,8 @@ def _load_from_module_spec(self, spec: importlib.machinery.ModuleSpec, key: str) def _resolve_name(self, name: str, package: Optional[str]) -> str: try: return importlib.util.resolve_name(name, package) - except ImportError as e: - raise errors.ExtensionNotFound(name) from e + except ImportError: + raise errors.ExtensionNotFound(name) def load_extension(self, name: str, *, package: Optional[str] = None) -> None: """Loads an extension. diff --git a/discord/commands/context.py b/discord/commands/context.py index 90bbbade3a..11150b3d1b 100644 --- a/discord/commands/context.py +++ b/discord/commands/context.py @@ -223,12 +223,10 @@ def send_modal(self) -> Callable[..., Awaitable[Interaction]]: async def respond(self, *args, **kwargs) -> Union[Interaction, WebhookMessage]: """Sends either a response or a followup response depending if the interaction has been responded to yet or not.""" try: - return ( - await self.followup.send(*args, **kwargs) - if self.interaction.response.is_done() - else await self.interaction.response.send_message(*args, **kwargs) - ) - + if not self.interaction.response.is_done(): + return await self.interaction.response.send_message(*args, **kwargs) # self.response + else: + return await self.followup.send(*args, **kwargs) # self.send_followup except discord.errors.InteractionResponded: return await self.followup.send(*args, **kwargs) @@ -289,7 +287,10 @@ def edit(self) -> Callable[..., Awaitable[InteractionMessage]]: @property def cog(self) -> Optional[Cog]: """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. ``None`` if it does not exist.""" - return None if self.command is None else self.command.cog + if self.command is None: + return None + + return self.command.cog class AutocompleteContext: @@ -329,4 +330,7 @@ def __init__(self, bot: Bot, interaction: Interaction): @property def cog(self) -> Optional[Cog]: """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. ``None`` if it does not exist.""" - return None if self.command is None else self.command.cog + if self.command is None: + return None + + return self.command.cog diff --git a/discord/commands/core.py b/discord/commands/core.py index ba5a1fb203..76db34cfae 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -23,7 +23,6 @@ DEALINGS IN THE SOFTWARE. """ - from __future__ import annotations import asyncio @@ -92,7 +91,10 @@ CogT = TypeVar("CogT", bound="Cog") Coro = TypeVar("Coro", bound=Callable[..., Coroutine[Any, Any, Any]]) -P = ParamSpec("P") if TYPE_CHECKING else TypeVar("P") +if TYPE_CHECKING: + P = ParamSpec("P") +else: + P = TypeVar("P") def wrap_callback(coro): @@ -260,7 +262,9 @@ def _prepare_cooldowns(self, ctx: ApplicationContext): bucket = self._buckets.get_bucket(ctx, current) # type: ignore # ctx instead of non-existent message if bucket is not None: - if retry_after := bucket.update_rate_limit(current): + retry_after = bucket.update_rate_limit(current) + + if retry_after: from ..ext.commands.errors import CommandOnCooldown raise CommandOnCooldown(bucket, retry_after, self._buckets.type) # type: ignore @@ -478,9 +482,10 @@ async def call_before_hooks(self, ctx: ApplicationContext) -> None: cog = self.cog if self._before_invoke is not None: # should be cog if @commands.before_invoke is used + instance = getattr(self._before_invoke, "__self__", cog) # __self__ only exists for methods, not functions # however, if @command.before_invoke is used, it will be a function - if instance := getattr(self._before_invoke, "__self__", cog): + if instance: await self._before_invoke(instance, ctx) # type: ignore else: await self._before_invoke(ctx) # type: ignore @@ -499,7 +504,8 @@ async def call_before_hooks(self, ctx: ApplicationContext) -> None: async def call_after_hooks(self, ctx: ApplicationContext) -> None: cog = self.cog if self._after_invoke is not None: - if instance := getattr(self._after_invoke, "__self__", cog): + instance = getattr(self._after_invoke, "__self__", cog) + if instance: await self._after_invoke(instance, ctx) # type: ignore else: await self._after_invoke(ctx) # type: ignore @@ -542,7 +548,9 @@ def qualified_name(self) -> str: ``one two three``. """ - if parent := self.full_parent_name: + parent = self.full_parent_name + + if parent: return f"{parent} {self.name}" else: return self.name @@ -658,8 +666,8 @@ def _check_required_params(self, params): for p in required_params: try: next(params) - except StopIteration as e: - raise ClientException(f'Callback for {self.name} command is missing "{p}" parameter.') from e + except StopIteration: + raise ClientException(f'Callback for {self.name} command is missing "{p}" parameter.') return params @@ -722,8 +730,8 @@ def _match_option_param_names(self, params, options): _validate_descriptions(o) try: p_name, p_obj = next(params) - except StopIteration as e: # not enough params for all the options - raise ClientException("Too many arguments passed to the options kwarg.") from e + except StopIteration: # not enough params for all the options + raise ClientException("Too many arguments passed to the options kwarg.") p_obj = p_obj.annotation if not any(check(o, p_obj) for check in check_annotations): @@ -867,7 +875,7 @@ async def invoke_autocomplete_callback(self, ctx: AutocompleteContext): for op in ctx.interaction.data.get("options", []): if op.get("focused", False): option = find(lambda o: o.name == op["name"], self.options) - values |= {i["name"]: i["value"] for i in ctx.interaction.data["options"]} + values.update({i["name"]: i["value"] for i in ctx.interaction.data["options"]}) ctx.command = self ctx.focused = option ctx.value = op.get("value") @@ -916,7 +924,7 @@ def _ensure_assignment_on_copy(self, other): def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() - kw |= self.__original_kwargs__ + kw.update(self.__original_kwargs__) copy = self.__class__(self.callback, **kw) return self._ensure_assignment_on_copy(copy) else: @@ -991,7 +999,7 @@ def __init__( ) -> None: validate_chat_input_name(name) validate_chat_input_description(description) - self.name = name + self.name = str(name) self.description = description self.input_type = SlashCommandOptionType.sub_command_group self.subcommands: List[Union[SlashCommand, SlashCommandGroup]] = self.__initial_commands__ @@ -1186,7 +1194,7 @@ def _ensure_assignment_on_copy(self, other): def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() - kw |= self.__original_kwargs__ + kw.update(self.__original_kwargs__) copy = self.__class__(self.callback, **kw) return self._ensure_assignment_on_copy(copy) else: @@ -1277,15 +1285,15 @@ def validate_parameters(self): # next we have the 'ctx' as the next parameter try: next(params) - except StopIteration as e: - raise ClientException(f'Callback for {self.name} command is missing "ctx" parameter.') from e + except StopIteration: + raise ClientException(f'Callback for {self.name} command is missing "ctx" parameter.') # next we have the 'user/message' as the next parameter try: next(params) - except StopIteration as exc: + except StopIteration: cmd = "user" if type(self) == UserCommand else "message" - raise ClientException(f'Callback for {self.name} command is missing "{cmd}" parameter.') from exc + raise ClientException(f'Callback for {self.name} command is missing "{cmd}" parameter.') # next there should be no more parameters try: @@ -1408,7 +1416,7 @@ def _ensure_assignment_on_copy(self, other): def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() - kw |= self.__original_kwargs__ + kw.update(self.__original_kwargs__) copy = self.__class__(self.callback, **kw) return self._ensure_assignment_on_copy(copy) else: @@ -1505,7 +1513,7 @@ def _ensure_assignment_on_copy(self, other): def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() - kw |= self.__original_kwargs__ + kw.update(self.__original_kwargs__) copy = self.__class__(self.callback, **kw) return self._ensure_assignment_on_copy(copy) else: diff --git a/discord/components.py b/discord/components.py index 2401b81438..695647191e 100644 --- a/discord/components.py +++ b/discord/components.py @@ -399,12 +399,12 @@ def __init__( ) -> None: if len(label) > 100: raise ValueError("label must be 100 characters or fewer") - if value != MISSING and len(value) > 100: + if value is not MISSING and len(value) > 100: raise ValueError("value must be 100 characters or fewer") if description is not None and len(description) > 100: raise ValueError("description must be 100 characters or fewer") self.label = label - self.value = label if value == MISSING else value + self.value = label if value is MISSING else value self.description = description if emoji is not None: diff --git a/discord/embeds.py b/discord/embeds.py index e713aa43be..07cebbfec4 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -723,7 +723,7 @@ def add_field(self: E, *, name: str, value: str, inline: bool = True) -> E: inline: :class:`bool` Whether the field should be displayed inline. """ - self._fields.append(EmbedField(name=name, value=value, inline=inline)) + self._fields.append(EmbedField(name=str(name), value=str(value), inline=inline)) return self @@ -809,8 +809,8 @@ def set_field_at(self: E, index: int, *, name: Any, value: Any, inline: bool = T try: field = self._fields[index] - except (TypeError, IndexError) as e: - raise IndexError("field index out of range") from e + except (TypeError, IndexError): + raise IndexError("field index out of range") field.name = str(name) field.value = str(value) diff --git a/discord/emoji.py b/discord/emoji.py index ea811717a0..0b2cf788d3 100644 --- a/discord/emoji.py +++ b/discord/emoji.py @@ -253,7 +253,7 @@ async def edit( """ payload = {} - if name != MISSING: + if name is not MISSING: payload["name"] = name if roles is not MISSING: payload["roles"] = [role.id for role in roles] diff --git a/discord/ext/bridge/core.py b/discord/ext/bridge/core.py index 8a391db01c..f9aa6f7548 100644 --- a/discord/ext/bridge/core.py +++ b/discord/ext/bridge/core.py @@ -86,7 +86,8 @@ def get_ext_command(self): :class:`BridgeExtCommand` The respective traditional (prefix-based) version of the command. """ - return BridgeExtCommand(self.callback, **self.kwargs) + command = BridgeExtCommand(self.callback, **self.kwargs) + return command def get_application_command(self): """A method to get the discord.commands version of this command. @@ -96,7 +97,8 @@ def get_application_command(self): :class:`BridgeSlashCommand` The respective slash command version of the command. """ - return BridgeSlashCommand(self.callback, **self.kwargs) + command = BridgeSlashCommand(self.callback, **self.kwargs) + return command def add_to(self, bot: Union[ExtBot, ExtAutoShardedBot]) -> None: """Adds the command to a bot. diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index fe8de9cbd3..121ebdf438 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -231,7 +231,7 @@ async def get_prefix(self, message: Message) -> Union[List[str], str]: if not isinstance(ret, str): try: ret = list(ret) - except TypeError as e: + except TypeError: # It's possible that a generator raised this exception. Don't # replace it with our own error if that's the case. if isinstance(ret, collections.abc.Iterable): @@ -240,7 +240,7 @@ async def get_prefix(self, message: Message) -> Union[List[str], str]: raise TypeError( "command_prefix must be plain string, iterable of strings, or callable " f"returning either of these, not {ret.__class__.__name__}" - ) from e + ) if not ret: raise ValueError("Iterable command_prefix must contain at least one prefix") @@ -298,12 +298,12 @@ class be provided, it must be similar enough to :class:`.Context`\'s else: return ctx - except TypeError as e: + except TypeError: if not isinstance(prefix, list): raise TypeError( "get_prefix must return either a string or a list of string, " f"not {prefix.__class__.__name__}" - ) from e + ) # It's possible a bad command_prefix got us here. for value in prefix: @@ -311,7 +311,7 @@ class be provided, it must be similar enough to :class:`.Context`\'s raise TypeError( "Iterable command_prefix or list returned from get_prefix must " f"contain only strings, not {value.__class__.__name__}" - ) from e + ) # Getting here shouldn't happen raise diff --git a/discord/ext/commands/context.py b/discord/ext/commands/context.py index b6a5e32dcd..de2097f3f3 100644 --- a/discord/ext/commands/context.py +++ b/discord/ext/commands/context.py @@ -268,14 +268,16 @@ def clean_prefix(self) -> str: # consider this to be an *incredibly* strange use case. I'd rather go # for this common use case rather than waste performance for the # odd one. - pattern = re.compile(f"<@!?{user.id}>") + pattern = re.compile(r"<@!?%s>" % user.id) return pattern.sub("@%s" % user.display_name.replace("\\", r"\\"), self.prefix) @property def cog(self) -> Optional[Cog]: """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. None if it does not exist.""" - return None if self.command is None else self.command.cog + if self.command is None: + return None + return self.command.cog @discord.utils.cached_property def guild(self) -> Optional[Guild]: @@ -352,7 +354,7 @@ async def send_help(self, *args: Any) -> Any: cmd = cmd.copy() cmd.context = self - if not args: + if len(args) == 0: await cmd.prepare_help_command(self, None) mapping = cmd.get_bot_mapping() injected = wrap_callback(cmd.send_bot_help) diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py index 0415e02ba2..c40aaef90a 100644 --- a/discord/ext/commands/converter.py +++ b/discord/ext/commands/converter.py @@ -363,10 +363,14 @@ def _get_id_matches(ctx, argument): @staticmethod def _resolve_channel(ctx, guild_id, channel_id) -> Optional[PartialMessageableChannel]: - if guild_id is None: + if guild_id is not None: + guild = ctx.bot.get_guild(guild_id) + if guild is not None and channel_id is not None: + return guild._resolve_channel(channel_id) # type: ignore + else: + return None + else: return ctx.bot.get_channel(channel_id) if channel_id else ctx.channel - guild = ctx.bot.get_guild(guild_id) - return guild._resolve_channel(channel_id) if guild is not None and channel_id is not None else None async def convert(self, ctx: Context, argument: str) -> discord.PartialMessage: guild_id, message_id, channel_id = self._get_id_matches(ctx, argument) @@ -393,17 +397,18 @@ class MessageConverter(IDConverter[discord.Message]): async def convert(self, ctx: Context, argument: str) -> discord.Message: guild_id, message_id, channel_id = PartialMessageConverter._get_id_matches(ctx, argument) - if message := ctx.bot._connection._get_message(message_id): + message = ctx.bot._connection._get_message(message_id) + if message: return message channel = PartialMessageConverter._resolve_channel(ctx, guild_id, channel_id) if not channel: raise ChannelNotFound(channel_id) try: return await channel.fetch_message(message_id) - except discord.NotFound as e: - raise MessageNotFound(argument) from e - except discord.Forbidden as e: - raise ChannelNotReadable(channel) from e + except discord.NotFound: + raise MessageNotFound(argument) + except discord.Forbidden: + raise ChannelNotReadable(channel) class GuildChannelConverter(IDConverter[discord.abc.GuildChannel]): @@ -610,8 +615,8 @@ def parse_hex_number(self, argument): value = int(arg, base=16) if not (0 <= value <= 0xFFFFFF): raise BadColourArgument(argument) - except ValueError as e: - raise BadColourArgument(argument) from e + except ValueError: + raise BadColourArgument(argument) else: return discord.Color(value=value) @@ -641,7 +646,7 @@ async def convert(self, ctx: Context, argument: str) -> discord.Colour: if argument[0] == "#": return self.parse_hex_number(argument[1:]) - if argument.startswith("0x"): + if argument[0:2] == "0x": rest = argument[2:] # Legacy backwards compatible syntax if rest.startswith("#"): @@ -649,7 +654,7 @@ async def convert(self, ctx: Context, argument: str) -> discord.Colour: return self.parse_hex_number(rest) arg = argument.lower() - if arg.startswith("rgb"): + if arg[0:3] == "rgb": return self.parse_rgb(arg) arg = arg.replace(" ", "_") @@ -683,7 +688,8 @@ async def convert(self, ctx: Context, argument: str) -> discord.Role: if not guild: raise NoPrivateMessage() - if match := self._get_id_match(argument) or re.match(r"<@&([0-9]{15,20})>$", argument): + match = self._get_id_match(argument) or re.match(r"<@&([0-9]{15,20})>$", argument) + if match: result = guild.get_role(int(match.group(1))) else: result = discord.utils.get(guild._roles.values(), name=argument) @@ -739,8 +745,8 @@ async def convert(self, ctx: Context, argument: str) -> discord.Guild: if result is None: result = discord.utils.get(ctx.bot.guilds, name=argument) - if result is None: - raise GuildNotFound(argument) + if result is None: + raise GuildNotFound(argument) return result @@ -795,7 +801,9 @@ class PartialEmojiConverter(Converter[discord.PartialEmoji]): """ async def convert(self, ctx: Context, argument: str) -> discord.PartialEmoji: - if match := re.match(r"<(a?):([a-zA-Z0-9\_]{1,32}):([0-9]{15,20})>$", argument): + match = re.match(r"<(a?):([a-zA-Z0-9\_]{1,32}):([0-9]{15,20})>$", argument) + + if match: emoji_animated = bool(match.group(1)) emoji_name = match.group(2) emoji_id = int(match.group(3)) @@ -923,9 +931,10 @@ def resolve_channel(id: int) -> str: } def repl(match: re.Match) -> str: - transform_type = match[1] - transform_id = int(match[2]) - return transforms[transform_type](transform_id) + type = match[1] + id = int(match[2]) + transformed = transforms[type](id) + return transformed result = re.sub(r"<(@[!&]?|#)([0-9]{15,20})>", repl, argument) if self.escape_markdown: @@ -992,9 +1001,9 @@ def __class_getitem__(cls, params: Union[Tuple[T], T]) -> Greedy[T]: def _convert_to_bool(argument: str) -> bool: lowered = argument.lower() - if lowered in {"yes", "y", "true", "t", "1", "enable", "on"}: + if lowered in ("yes", "y", "true", "t", "1", "enable", "on"): return True - elif lowered in {"no", "n", "false", "f", "0", "disable", "off"}: + elif lowered in ("no", "n", "false", "f", "0", "disable", "off"): return False else: raise BadBoolArgument(lowered) diff --git a/discord/ext/commands/cooldowns.py b/discord/ext/commands/cooldowns.py index 29493da741..7fb348833e 100644 --- a/discord/ext/commands/cooldowns.py +++ b/discord/ext/commands/cooldowns.py @@ -96,7 +96,7 @@ class Cooldown: def __init__(self, rate: float, per: float) -> None: self.rate: int = int(rate) - self.per: float = per + self.per: float = float(per) self._window: float = 0.0 self._tokens: int = self.rate self._last: float = 0.0 diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index 8f41154a4e..676bd34b02 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -528,7 +528,7 @@ def copy(self: CommandT) -> CommandT: def _update_copy(self: CommandT, kwargs: Dict[str, Any]) -> CommandT: if kwargs: kw = kwargs.copy() - kw |= self.__original_kwargs__ + kw.update(self.__original_kwargs__) copy = self.__class__(self.callback, **kw) return self._ensure_assignment_on_copy(copy) else: @@ -704,7 +704,9 @@ def root_parent(self) -> Optional[Group]: For example in commands ``?a b c test``, the root parent is ``a``. """ - return self.parents[-1] if self.parent else None + if not self.parent: + return None + return self.parents[-1] @property def qualified_name(self) -> str: @@ -738,8 +740,8 @@ async def _parse_arguments(self, ctx: Context) -> None: # the iterator and resume parsing try: next(iterator) - except StopIteration as e: - raise discord.ClientException(f'Callback for {self.name} command is missing "self" parameter.') from e + except StopIteration: + raise discord.ClientException(f'Callback for {self.name} command is missing "self" parameter.') # next we have the 'ctx' as the next parameter try: @@ -780,9 +782,10 @@ async def call_before_hooks(self, ctx: Context) -> None: cog = self.cog if self._before_invoke is not None: # should be cog if @commands.before_invoke is used + instance = getattr(self._before_invoke, "__self__", cog) # __self__ only exists for methods, not functions # however, if @command.before_invoke is used, it will be a function - if instance := getattr(self._before_invoke, "__self__", cog): + if instance: await self._before_invoke(instance, ctx) # type: ignore else: await self._before_invoke(ctx) # type: ignore @@ -801,7 +804,8 @@ async def call_before_hooks(self, ctx: Context) -> None: async def call_after_hooks(self, ctx: Context) -> None: cog = self.cog if self._after_invoke is not None: - if instance := getattr(self._after_invoke, "__self__", cog): + instance = getattr(self._after_invoke, "__self__", cog) + if instance: await self._after_invoke(instance, ctx) # type: ignore else: await self._after_invoke(ctx) # type: ignore @@ -822,7 +826,8 @@ def _prepare_cooldowns(self, ctx: Context) -> None: current = dt.replace(tzinfo=datetime.timezone.utc).timestamp() bucket = self._buckets.get_bucket(ctx.message, current) if bucket is not None: - if retry_after := bucket.update_rate_limit(current): + retry_after = bucket.update_rate_limit(current) + if retry_after: raise CommandOnCooldown(bucket, retry_after, self._buckets.type) # type: ignore async def prepare(self, ctx: Context) -> None: @@ -1083,8 +1088,7 @@ def signature(self) -> str: # do [name] since [name=None] or [name=] are not exactly useful for the user. should_print = param.default if isinstance(param.default, str) else param.default is not None if should_print: - result.append(f"[{name}={param.default}]..." if greedy else f"[{name}={param.default}]") - + result.append(f"[{name}={param.default}]" if not greedy else f"[{name}={param.default}]...") continue else: result.append(f"[{name}]") diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py index 26eabb614a..5d7cccb2cd 100644 --- a/discord/ext/tasks/__init__.py +++ b/discord/ext/tasks/__init__.py @@ -599,7 +599,12 @@ def _prepare_time_index(self, now: datetime.datetime = MISSING) -> None: time_now = ( now if now is not MISSING else datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0) ).timetz() - self._time_index = next((idx for idx, time in enumerate(self._time) if time >= time_now), 0) + for idx, time in enumerate(self._time): + if time >= time_now: + self._time_index = idx + break + else: + self._time_index = 0 def _get_time_parameter( self, diff --git a/discord/sinks/core.py b/discord/sinks/core.py index c01d430d56..9685262e1b 100644 --- a/discord/sinks/core.py +++ b/discord/sinks/core.py @@ -22,7 +22,6 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ - import io import os import struct @@ -45,7 +44,12 @@ ) -CREATE_NO_WINDOW = 0 if sys.platform != "win32" else 0x08000000 +if sys.platform != "win32": + CREATE_NO_WINDOW = 0 +else: + CREATE_NO_WINDOW = 0x08000000 + + default_filters = { "time": 0, "users": [], diff --git a/discord/ui/button.py b/discord/ui/button.py index 545410e837..6f12a4d7da 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -175,7 +175,7 @@ def disabled(self) -> bool: @disabled.setter def disabled(self, value: bool): - self._underlying.disabled = value + self._underlying.disabled = bool(value) @property def label(self) -> Optional[str]: diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index f0409f7e69..7ca41dfd53 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -587,15 +587,15 @@ def handle_message_parameters( } ) else: - multipart.extend( - { - "name": f"file{index}", - "value": file.fp, - "filename": file.filename, - "content_type": "application/octet-stream", - } - for index, file in enumerate(files) - ) + for index, file in enumerate(files): + multipart.append( + { + "name": f"file{index}", + "value": file.fp, + "filename": file.filename, + "content_type": "application/octet-stream", + } + ) return ExecuteWebhookParameters(payload=payload, multipart=multipart, files=files) @@ -679,7 +679,9 @@ def __init__(self, webhook: Any, parent: Optional[Union[ConnectionState, _Webhoo self._parent = None if isinstance(parent, _WebhookState) else parent def _get_guild(self, guild_id): - return self._parent._get_guild(guild_id) if self._parent is not None else None + if self._parent is not None: + return self._parent._get_guild(guild_id) + return None def store_user(self, data): if self._parent is not None: