Skip to content

typing: fix abc.User protocol structural subtyping #1051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1051.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix typing issue with :class:`abc.User` protocol requirements, which previously resulted in :class:`User` and :class:`Member` not conforming to the protocol.
10 changes: 7 additions & 3 deletions disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ class User(Snowflake, Protocol):

.. versionadded:: 2.9

avatar: :class:`~disnake.Asset`
The avatar asset the user has.
bot: :class:`bool`
Whether the user is a bot account.
"""
Expand All @@ -157,7 +155,6 @@ class User(Snowflake, Protocol):
name: str
discriminator: str
global_name: Optional[str]
avatar: Asset
bot: bool

@property
Expand All @@ -170,6 +167,13 @@ def mention(self) -> str:
""":class:`str`: Returns a string that allows you to mention the given user."""
raise NotImplementedError

@property
def avatar(self) -> Optional[Asset]:
"""Optional[:class:`~disnake.Asset`]: Returns an :class:`~disnake.Asset` for
the avatar the user has.
"""
raise NotImplementedError


@runtime_checkable
class PrivateChannel(Snowflake, Protocol):
Expand Down
15 changes: 2 additions & 13 deletions disnake/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class Member(disnake.abc.Messageable, _UserTag):
if TYPE_CHECKING:
name: str
id: int
discriminator: str
global_name: Optional[str]
bot: bool
system: bool
Expand Down Expand Up @@ -487,19 +488,7 @@ def status(self, value: Status) -> None:
@property
def tag(self) -> str:
""":class:`str`: An alias of :attr:`.discriminator`."""
return self._user.discriminator

@property
def discriminator(self) -> str:
""":class:`str`: The user's discriminator.

.. note::
This is being phased out by Discord; the username system is moving away from ``username#discriminator``
to users having a globally unique username.
The value of a single zero (``"0"``) indicates that the user has been migrated to the new system.
See the `help article <https://dis.gd/app-usernames>`__ for details.
"""
return self._user.discriminator
return self.discriminator

@property
def mobile_status(self) -> Status:
Expand Down
14 changes: 14 additions & 0 deletions tests/test_abc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: MIT

from typing import cast
from unittest import mock

import pytest
Expand Down Expand Up @@ -151,3 +152,16 @@ async def test_overwrites(self, channel, sync_permissions) -> None:
channel._state.http.edit_channel.assert_awaited_once_with(
channel.id, permission_overwrites=[], reason=None
)


class TestUserProtocol:
def _test_typing_assignable(self) -> None:
def handle_abc_user(user: disnake.abc.User) -> None:
...

# All of these should match the abc.User protocol and thus type-check correctly
# (they could just inherit from the protocol to ensure correct implementation,
# but we really only want structural (i.e. implicit) subtyping)
handle_abc_user(cast(disnake.User, ...))
handle_abc_user(cast(disnake.ClientUser, ...))
handle_abc_user(cast(disnake.Member, ...))