From 6d2e93acc65ea79b2eb1f1a7671d03d72318f364 Mon Sep 17 00:00:00 2001 From: CoderJoshDK <74162303+CoderJoshDK@users.noreply.github.com> Date: Fri, 17 Oct 2025 08:43:12 -0400 Subject: [PATCH 1/3] style: enable more pyright rules Add in reportInconsistentOverload and reportInvalidTypeVarUse --- disnake/enums.py | 4 ++-- disnake/ext/tasks/__init__.py | 3 ++- disnake/i18n.py | 12 +++++------ disnake/ui/button.py | 30 ---------------------------- disnake/ui/item.py | 6 ------ disnake/ui/select/channel.py | 32 ------------------------------ disnake/ui/select/mentionable.py | 34 -------------------------------- disnake/ui/select/role.py | 30 ---------------------------- disnake/ui/select/string.py | 30 ---------------------------- disnake/ui/select/user.py | 30 ---------------------------- disnake/ui/view.py | 2 +- pyproject.toml | 5 ----- 12 files changed, 11 insertions(+), 207 deletions(-) diff --git a/disnake/enums.py b/disnake/enums.py index 533b3c5447..8fec1096ed 100644 --- a/disnake/enums.py +++ b/disnake/enums.py @@ -167,10 +167,10 @@ def __new__( value_cls._actual_enum_cls_ = actual_cls = super().__new__(cls, name, bases, attrs) return actual_cls - def __iter__(cls) -> Iterator[EnumMetaT]: + def __iter__(cls) -> Iterator[EnumMeta]: return (cls._enum_member_map_[name] for name in cls._enum_member_names_) - def __reversed__(cls) -> Iterator[EnumMetaT]: + def __reversed__(cls) -> Iterator[EnumMeta]: return (cls._enum_member_map_[name] for name in reversed(cls._enum_member_names_)) def __len__(cls) -> int: diff --git a/disnake/ext/tasks/__init__.py b/disnake/ext/tasks/__init__.py index a28d8196c7..22243e0b80 100644 --- a/disnake/ext/tasks/__init__.py +++ b/disnake/ext/tasks/__init__.py @@ -737,7 +737,8 @@ def loop( def loop( cls: type[Object[L_co, Concatenate[LF, P]]] = Loop[Any], - **kwargs: Any, + *_: P.args, + **kwargs: P.kwargs, ) -> Callable[[LF], L_co]: """A decorator that schedules a task in the background for you with optional reconnect logic. The decorator returns a :class:`Loop`. diff --git a/disnake/i18n.py b/disnake/i18n.py index 88fed77b94..c799e5c1d7 100644 --- a/disnake/i18n.py +++ b/disnake/i18n.py @@ -84,14 +84,14 @@ class Localized(Generic[StringT]): __slots__ = ("string", "localizations") @overload - def __init__(self: Localized[StringT], string: StringT, *, key: str) -> None: ... + def __init__(self, string: StringT, *, key: str) -> None: ... @overload - def __init__(self: Localized[Optional[str]], *, key: str) -> None: ... + def __init__(self, *, key: str) -> None: ... @overload def __init__( - self: Localized[StringT], + self, string: StringT, *, data: Union[Optional[LocalizationsDict], LocalizationValue], @@ -99,7 +99,7 @@ def __init__( @overload def __init__( - self: Localized[Optional[str]], + self, *, data: Union[Optional[LocalizationsDict], LocalizationValue], ) -> None: ... @@ -108,12 +108,12 @@ def __init__( # as it's only meant to be used internally def __init__( self, - string: StringT = None, + string: Optional[StringT] = None, *, key: str = MISSING, data: Union[Optional[LocalizationsDict], LocalizationValue] = MISSING, ) -> None: - self.string: StringT = string + self.string: StringT = string # pyright: ignore[reportAttributeAccessIssue] if not (key is MISSING) ^ (data is MISSING): msg = "Exactly one of `key` or `data` must be provided" diff --git a/disnake/ui/button.py b/disnake/ui/button.py index 7e32a23b47..21efb408fd 100644 --- a/disnake/ui/button.py +++ b/disnake/ui/button.py @@ -83,36 +83,6 @@ class Button(Item[V_co]): # We have to set this to MISSING in order to overwrite the abstract property from UIComponent _underlying: ButtonComponent = MISSING - @overload - def __init__( - self: Button[None], - *, - style: ButtonStyle = ButtonStyle.secondary, - label: Optional[str] = None, - disabled: bool = False, - custom_id: Optional[str] = None, - url: Optional[str] = None, - emoji: Optional[Union[str, Emoji, PartialEmoji]] = None, - sku_id: Optional[int] = None, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: Button[V_co], - *, - style: ButtonStyle = ButtonStyle.secondary, - label: Optional[str] = None, - disabled: bool = False, - custom_id: Optional[str] = None, - url: Optional[str] = None, - emoji: Optional[Union[str, Emoji, PartialEmoji]] = None, - sku_id: Optional[int] = None, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/item.py b/disnake/ui/item.py index be6a4a8d91..ad4fbb2f85 100644 --- a/disnake/ui/item.py +++ b/disnake/ui/item.py @@ -159,12 +159,6 @@ class Item(WrappedComponent, Generic[V_co]): __repr_attributes__: ClassVar[tuple[str, ...]] = ("row",) - @overload - def __init__(self: Item[None]) -> None: ... - - @overload - def __init__(self: Item[V_co]) -> None: ... - def __init__(self) -> None: self._view: V_co = None # pyright: ignore[reportAttributeAccessIssue] self._row: Optional[int] = None diff --git a/disnake/ui/select/channel.py b/disnake/ui/select/channel.py index d5a9bca2a1..8589230f11 100644 --- a/disnake/ui/select/channel.py +++ b/disnake/ui/select/channel.py @@ -109,38 +109,6 @@ class ChannelSelect(BaseSelect[ChannelSelectMenu, "AnyChannel", V_co]): ), } - @overload - def __init__( - self: ChannelSelect[None], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - channel_types: Optional[list[ChannelType]] = None, - default_values: Optional[Sequence[SelectDefaultValueInputType[AnyChannel]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: ChannelSelect[V_co], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - channel_types: Optional[list[ChannelType]] = None, - default_values: Optional[Sequence[SelectDefaultValueInputType[AnyChannel]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/select/mentionable.py b/disnake/ui/select/mentionable.py index 784fc72edb..d64b89599e 100644 --- a/disnake/ui/select/mentionable.py +++ b/disnake/ui/select/mentionable.py @@ -97,40 +97,6 @@ class MentionableSelect(BaseSelect[MentionableSelectMenu, "Union[User, Member, R SelectDefaultValueType.role: (Role,), } - @overload - def __init__( - self: MentionableSelect[None], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[ - Sequence[SelectDefaultValueMultiInputType[Union[User, Member, Role]]] - ] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: MentionableSelect[V_co], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[ - Sequence[SelectDefaultValueMultiInputType[Union[User, Member, Role]]] - ] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/select/role.py b/disnake/ui/select/role.py index b9105868ea..2c482988df 100644 --- a/disnake/ui/select/role.py +++ b/disnake/ui/select/role.py @@ -92,36 +92,6 @@ class RoleSelect(BaseSelect[RoleSelectMenu, "Role", V_co]): SelectDefaultValueType.role: (Role, Object), } - @overload - def __init__( - self: RoleSelect[None], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[Sequence[SelectDefaultValueInputType[Role]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: RoleSelect[V_co], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[Sequence[SelectDefaultValueInputType[Role]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/select/string.py b/disnake/ui/select/string.py index 387c2ae290..35875569e8 100644 --- a/disnake/ui/select/string.py +++ b/disnake/ui/select/string.py @@ -113,36 +113,6 @@ class StringSelect(BaseSelect[StringSelectMenu, str, V_co]): Mapping[SelectDefaultValueType, tuple[type[Snowflake], ...]] ] = {} - @overload - def __init__( - self: StringSelect[None], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - options: SelectOptionInput = ..., - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: StringSelect[V_co], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - options: SelectOptionInput = ..., - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/select/user.py b/disnake/ui/select/user.py index a8c2f85e0c..83e9460354 100644 --- a/disnake/ui/select/user.py +++ b/disnake/ui/select/user.py @@ -94,36 +94,6 @@ class UserSelect(BaseSelect[UserSelectMenu, "Union[User, Member]", V_co]): SelectDefaultValueType.user: (Member, User, ClientUser, Object), } - @overload - def __init__( - self: UserSelect[None], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[Sequence[SelectDefaultValueInputType[Union[User, Member]]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - - @overload - def __init__( - self: UserSelect[V_co], - *, - custom_id: str = ..., - placeholder: Optional[str] = None, - min_values: int = 1, - max_values: int = 1, - disabled: bool = False, - default_values: Optional[Sequence[SelectDefaultValueInputType[Union[User, Member]]]] = None, - required: bool = True, - id: int = 0, - row: Optional[int] = None, - ) -> None: ... - def __init__( self, *, diff --git a/disnake/ui/view.py b/disnake/ui/view.py index 786f56208f..d591b6a3ff 100644 --- a/disnake/ui/view.py +++ b/disnake/ui/view.py @@ -173,7 +173,7 @@ async def __timeout_task_impl(self) -> None: await asyncio.sleep(self.__timeout_expiry - now) def to_components(self) -> list[ActionRowPayload]: - def key(item: Item) -> int: + def key(item: Item[View]) -> int: return item._rendered_row or 0 children = sorted(self.children, key=key) diff --git a/pyproject.toml b/pyproject.toml index 32974ff76d..4255499c44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -371,11 +371,6 @@ reportImportCycles = false reportIncompatibleMethodOverride = false reportIncompatibleVariableOverride = false -# TODO: re-enable -# these were added in the upgrade from pyright 1.1.336 to 1.1.405 -reportInconsistentOverload = false -reportInvalidTypeVarUse = false - # these are largely due to missing type hints, and make up most of the error count reportUnknownMemberType = false reportUnknownParameterType = false From d0abf4658bcbfddea09a6b2e84f43ef698c3470d Mon Sep 17 00:00:00 2001 From: CoderJoshDK <74162303+CoderJoshDK@users.noreply.github.com> Date: Fri, 17 Oct 2025 09:13:25 -0400 Subject: [PATCH 2/3] chore: add changelog --- changelog/1441.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/1441.misc.rst diff --git a/changelog/1441.misc.rst b/changelog/1441.misc.rst new file mode 100644 index 0000000000..b8f506e286 --- /dev/null +++ b/changelog/1441.misc.rst @@ -0,0 +1 @@ +Enable `reportInconsistentOverload` and `reportInvalidTypeVarUse` pyright rules. From 7ef84940aceafb8724309e0fdc7030c163d727fb Mon Sep 17 00:00:00 2001 From: CoderJoshDK <74162303+CoderJoshDK@users.noreply.github.com> Date: Fri, 17 Oct 2025 21:30:27 -0400 Subject: [PATCH 3/3] WIP: provide typing default for optional typevar --- disnake/ext/commands/bot_base.py | 7 +++-- disnake/ext/commands/context.py | 10 ++++++- disnake/ext/commands/core.py | 15 ++++++---- disnake/ui/_types.py | 9 ++++-- disnake/ui/button.py | 9 ++++-- disnake/ui/item.py | 20 +++++++++---- disnake/ui/select/base.py | 9 ++++-- disnake/ui/view.py | 51 +++++++++++++++++++------------- 8 files changed, 90 insertions(+), 40 deletions(-) diff --git a/disnake/ext/commands/bot_base.py b/disnake/ext/commands/bot_base.py index a5543cde78..8ceccae733 100644 --- a/disnake/ext/commands/bot_base.py +++ b/disnake/ext/commands/bot_base.py @@ -10,7 +10,7 @@ import traceback import warnings from collections.abc import Iterable -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, cast import disnake from disnake.utils import iscoroutinefunction @@ -26,6 +26,7 @@ if TYPE_CHECKING: from typing_extensions import Self + from disnake.ext.commands.bot import AutoShardedBot, Bot from disnake.message import Message from ._types import Check, CoroFunc, MaybeCoro @@ -507,7 +508,9 @@ class be provided, it must be similar enough to :class:`.Context`\'s ``cls`` parameter. """ view = StringView(message.content) - ctx = cls(prefix=None, view=view, bot=self, message=message) + ctx = cls( + prefix=None, view=view, bot=cast("Union[Bot, AutoShardedBot]", self), message=message + ) if message.author.id == self.user.id: # pyright: ignore[reportAttributeAccessIssue] return ctx diff --git a/disnake/ext/commands/context.py b/disnake/ext/commands/context.py index 4038704937..49aa2eb6cc 100644 --- a/disnake/ext/commands/context.py +++ b/disnake/ext/commands/context.py @@ -32,13 +32,21 @@ T = TypeVar("T") -BotT = TypeVar("BotT", bound="Union[Bot, AutoShardedBot]") CogT = TypeVar("CogT", bound="Cog") if TYPE_CHECKING: + from typing_extensions import TypeVar # noqa: TC004 + P = ParamSpec("P") + BotT = TypeVar( + "BotT", + bound="Union[Bot, AutoShardedBot]", + covariant=True, + default=Union[Bot, AutoShardedBot], + ) else: P = TypeVar("P") + BotT = TypeVar("BotT", bound="Union[Bot, AutoShardedBot]") class Context(disnake.abc.Messageable, Generic[BotT]): diff --git a/disnake/ext/commands/core.py b/disnake/ext/commands/core.py index eed157e65c..bf7faba5f2 100644 --- a/disnake/ext/commands/core.py +++ b/disnake/ext/commands/core.py @@ -97,25 +97,30 @@ MISSING: Any = disnake.utils.MISSING -T = TypeVar("T") VT = TypeVar("VT") -CogT = TypeVar("CogT", bound="Optional[Cog]") CommandT = TypeVar("CommandT", bound="Command") -ContextT = TypeVar("ContextT", bound="Context") GroupT = TypeVar("GroupT", bound="Group") HookT = TypeVar("HookT", bound="Hook") ErrorT = TypeVar("ErrorT", bound="Error") if TYPE_CHECKING: - P = ParamSpec("P") + from typing_extensions import TypeVar # noqa: TC004 + P = ParamSpec("P", default=...) + T = TypeVar("T", default=Any) + + CogT = TypeVar("CogT", bound="Optional[Cog]", default="Optional[Cog]") + ContextT = TypeVar("ContextT", bound="Context", default="Context") CommandCallback = Union[ Callable[Concatenate[CogT, ContextT, P], Coro[T]], Callable[Concatenate[ContextT, P], Coro[T]], ] else: + T = TypeVar("T") P = TypeVar("P") + CogT = TypeVar("CogT", bound="Optional[Cog]") + ContextT = TypeVar("ContextT", bound="Context") def wrap_callback(coro: Callable[..., Coro[T]]) -> Callable[..., Coro[Optional[T]]]: @@ -1418,7 +1423,7 @@ def copy(self: GroupT) -> GroupT: """ ret = super().copy() for cmd in self.commands: - ret.add_command(cmd.copy()) + ret.add_command(cast("Command[CogT, Any, Any]", cmd.copy())) return ret async def invoke(self, ctx: Context) -> None: diff --git a/disnake/ui/_types.py b/disnake/ui/_types.py index 6e762e4a29..bedfecfbda 100644 --- a/disnake/ui/_types.py +++ b/disnake/ui/_types.py @@ -6,7 +6,10 @@ from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing_extensions import ( + TypeAlias, + TypeVar, # noqa: TC004 + ) from . import ( ActionRow, @@ -24,7 +27,9 @@ from .select import ChannelSelect, MentionableSelect, RoleSelect, StringSelect, UserSelect from .view import View -V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True, default=Optional[View]) +else: + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) AnySelect = Union[ "ChannelSelect[V_co]", diff --git a/disnake/ui/button.py b/disnake/ui/button.py index 21efb408fd..05be47cff5 100644 --- a/disnake/ui/button.py +++ b/disnake/ui/button.py @@ -17,18 +17,23 @@ ) if TYPE_CHECKING: - from typing_extensions import ParamSpec, Self + from typing_extensions import ( + ParamSpec, + Self, + TypeVar, # noqa: TC004 + ) from ..emoji import Emoji from .item import ItemCallbackType from .view import View + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True, default=Optional[View]) else: ParamSpec = TypeVar + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) B = TypeVar("B", bound="Button") B_co = TypeVar("B_co", bound="Button", covariant=True) -V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) P = ParamSpec("P") diff --git a/disnake/ui/item.py b/disnake/ui/item.py index ad4fbb2f85..9c8e0e2c9e 100644 --- a/disnake/ui/item.py +++ b/disnake/ui/item.py @@ -22,11 +22,12 @@ "Item", ) -I = TypeVar("I", bound="Item[Any]") # noqa: E741 -V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import ( + Self, + TypeVar, # noqa: TC004 + ) from ..client import Client from ..components import ActionRowChildComponent, Component @@ -35,8 +36,18 @@ from ..types.components import ActionRowChildComponent as ActionRowChildComponentPayload from .view import View + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True, default=Optional[View]) + I = TypeVar("I", bound="Item[Any]", default="Item[Any]") # noqa: E741 ItemCallbackType = Callable[[V_co, I, MessageInteraction], Coroutine[Any, Any, Any]] + SelfViewT = TypeVar("SelfViewT", bound="Optional[View]", default=Optional[View]) +else: + I = TypeVar("I", bound="Item[Any]") # noqa: E741 + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) + + SelfViewT = TypeVar("SelfViewT", bound="Optional[View]") + + ClientT = TypeVar("ClientT", bound="Client") UIComponentT = TypeVar("UIComponentT", bound="UIComponent") @@ -217,9 +228,6 @@ async def callback(self, interaction: MessageInteraction[ClientT], /) -> None: pass -SelfViewT = TypeVar("SelfViewT", bound="Optional[View]") - - # While the decorators don't actually return a descriptor that matches this protocol, # this protocol ensures that type checkers don't complain about statements like `self.button.disabled = True`, # which work as `View.__init__` replaces the handler with the item. diff --git a/disnake/ui/select/base.py b/disnake/ui/select/base.py index c7df7d5414..dfce390971 100644 --- a/disnake/ui/select/base.py +++ b/disnake/ui/select/base.py @@ -24,19 +24,24 @@ __all__ = ("BaseSelect",) if TYPE_CHECKING: - from typing_extensions import ParamSpec, Self + from typing_extensions import ( + ParamSpec, + Self, + TypeVar, # noqa: TC004 + ) from ...abc import Snowflake from ...interactions import MessageInteraction from ..item import ItemCallbackType from ..view import View + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True, default=Optional[View]) else: ParamSpec = TypeVar + V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) S_co = TypeVar("S_co", bound="BaseSelect", covariant=True) -V_co = TypeVar("V_co", bound="Optional[View]", covariant=True) SelectMenuT = TypeVar("SelectMenuT", bound=AnySelectMenu) SelectValueT = TypeVar("SelectValueT") P = ParamSpec("P") diff --git a/disnake/ui/view.py b/disnake/ui/view.py index d591b6a3ff..3a9e84688f 100644 --- a/disnake/ui/view.py +++ b/disnake/ui/view.py @@ -11,7 +11,14 @@ from collections.abc import Sequence from functools import partial from itertools import groupby -from typing import TYPE_CHECKING, Callable, ClassVar, Optional +from typing import ( + TYPE_CHECKING, + Callable, + ClassVar, + Generic, + Optional, + TypeVar, +) from ..components import ( VALID_ACTION_ROW_MESSAGE_COMPONENT_TYPES, @@ -38,29 +45,31 @@ from .item import ItemCallbackType +V_co = TypeVar("V_co", bound="View", covariant=True) + _log = logging.getLogger(__name__) -def _component_to_item(component: ActionRowMessageComponent) -> Item: +def _component_to_item(component: ActionRowMessageComponent) -> Item[V_co]: if item := _message_component_to_item(component): return item else: - return Item.from_component(component) + return Item[V_co].from_component(component) -class _ViewWeights: +class _ViewWeights(Generic[V_co]): __slots__ = ("weights",) - def __init__(self, children: list[Item]) -> None: + def __init__(self, children: list[Item[V_co]]) -> None: self.weights: list[int] = [0, 0, 0, 0, 0] - key: Callable[[Item[View]], int] = lambda i: sys.maxsize if i.row is None else i.row + key: Callable[[Item[V_co]], int] = lambda i: sys.maxsize if i.row is None else i.row children = sorted(children, key=key) for _, group in groupby(children, key=key): for item in group: self.add_item(item) - def find_open_space(self, item: Item) -> int: + def find_open_space(self, item: Item[V_co]) -> int: for index, weight in enumerate(self.weights): if weight + item.width <= 5: return index @@ -68,7 +77,7 @@ def find_open_space(self, item: Item) -> int: msg = "could not find open space for item" raise ValueError(msg) - def add_item(self, item: Item) -> None: + def add_item(self, item: Item[V_co]) -> None: if item.row is not None: total = self.weights[item.row] + item.width if total > 5: @@ -81,7 +90,7 @@ def add_item(self, item: Item) -> None: self.weights[index] += item.width item._rendered_row = index - def remove_item(self, item: Item) -> None: + def remove_item(self, item: Item[V_co]) -> None: if item._rendered_row is not None: self.weights[item._rendered_row] -= item.width item._rendered_row = None @@ -142,7 +151,7 @@ def __init__(self, *, timeout: Optional[float] = 180.0) -> None: setattr(self, func.__name__, item) self.children.append(item) - self.__weights = _ViewWeights(self.children) + self.__weights: _ViewWeights = _ViewWeights(self.children) loop = asyncio.get_running_loop() self.id: str = os.urandom(16).hex() self.__cancel_callback: Optional[Callable[[View], None]] = None @@ -173,7 +182,7 @@ async def __timeout_task_impl(self) -> None: await asyncio.sleep(self.__timeout_expiry - now) def to_components(self) -> list[ActionRowPayload]: - def key(item: Item[View]) -> int: + def key(item: Item[Self]) -> int: return item._rendered_row or 0 children = sorted(self.children, key=key) @@ -239,7 +248,7 @@ def _expires_at(self) -> Optional[float]: return time.monotonic() + self.timeout return None - def add_item(self, item: Item) -> Self: + def add_item(self, item: Item[Self]) -> Self: """Adds an item to the view. This function returns the class instance to allow for fluent-style @@ -272,7 +281,7 @@ def add_item(self, item: Item) -> Self: self.children.append(item) return self - def remove_item(self, item: Item) -> Self: + def remove_item(self, item: Item[Self]) -> Self: """Removes an item from the view. This function returns the class instance to allow for fluent-style @@ -336,7 +345,9 @@ async def on_timeout(self) -> None: """ pass - async def on_error(self, error: Exception, item: Item, interaction: MessageInteraction) -> None: + async def on_error( + self, error: Exception, item: Item[Self], interaction: MessageInteraction + ) -> None: """|coro| A callback that is called when an item's callback or :meth:`interaction_check` @@ -356,7 +367,7 @@ async def on_error(self, error: Exception, item: Item, interaction: MessageInter print(f"Ignoring exception in view {self} for item {item}:", file=sys.stderr) traceback.print_exception(error.__class__, error, error.__traceback__, file=sys.stderr) - async def _scheduled_task(self, item: Item, interaction: MessageInteraction) -> None: + async def _scheduled_task(self, item: Item[Self], interaction: MessageInteraction) -> None: try: if self.timeout: self.__timeout_expiry = time.monotonic() + self.timeout @@ -386,7 +397,7 @@ def _dispatch_timeout(self) -> None: self.__stopped.set_result(True) asyncio.create_task(self.on_timeout(), name=f"disnake-ui-view-timeout-{self.id}") - def _dispatch_item(self, item: Item, interaction: MessageInteraction) -> None: + def _dispatch_item(self, item: Item[Self], interaction: MessageInteraction) -> None: if self.__stopped.done(): return @@ -396,15 +407,15 @@ def _dispatch_item(self, item: Item, interaction: MessageInteraction) -> None: def refresh(self, components: list[ActionRowComponent[ActionRowMessageComponent]]) -> None: # TODO: this is pretty hacky at the moment, see https://github.com/DisnakeDev/disnake/commit/9384a72acb8c515b13a600592121357e165368da - old_state: dict[tuple[int, str], Item] = { + old_state: dict[tuple[int, str], Item[Self]] = { (item.type.value, item.custom_id): item # pyright: ignore[reportAttributeAccessIssue] for item in self.children if item.is_dispatchable() } - children: list[Item] = [] + children: list[Item[Self]] = [] for component in (c for row in components for c in row.children): - older: Optional[Item] = None + older: Optional[Item[Self]] = None try: older = old_state[component.type.value, component.custom_id] # pyright: ignore[reportArgumentType] except (KeyError, AttributeError): @@ -490,7 +501,7 @@ async def wait(self) -> bool: class ViewStore: def __init__(self, state: ConnectionState) -> None: # (component_type, message_id, custom_id): (View, Item) - self._views: dict[tuple[int, Optional[int], str], tuple[View, Item]] = {} + self._views: dict[tuple[int, int | None, str], tuple[View, Item[View]]] = {} # message_id: View self._synced_message_views: dict[int, View] = {} self._state: ConnectionState = state