Repository: Pycord-Development/pycord

Issue:
Breaking changes in the update on master from version py-cord 2.4.1.dev214+gfc7b1042
### ‼️ Developer Note

<!-- @plun1331 2024-02-27 -->

This bug appears to only affect those using `from __future__ import annotations`.

If you are not using stringified annotations, this bug should not affect you.

### Summary

When doing fresh install of py-cord the bot crash on start up. There is undocumented breaking changes

### Reproduction Steps

Install new version
Previous library version where the bot is working without any problem:
```
pip freeze 
aiofiles==23.2.1
aiohttp==3.8.6
aiolimiter==1.1.0
aiomysql==0.2.0
aiosignal==1.3.1
aiosqlite==0.19.0
async-timeout==4.0.3
attrs==23.2.0
certifi==2023.11.17
cffi==1.16.0
charset-normalizer==3.3.2
colorama==0.4.6
cryptography==41.0.7
frozenlist==1.4.1
idna==3.6
iniconfig==2.0.0
multidict==6.0.4
mysql-connector-python==8.2.0
packaging==23.2
pluggy==1.3.0
protobuf==4.21.12
psutil==5.9.7
py-cord @ git+https://github.com/Pycord-Development/pycord@fc7b1042c4a9a942b9996dfe96f56aac059e179c
pycparser==2.21
PyMySQL==1.1.0
pytest==7.4.4
pytest-asyncio==0.23.3
python-dateutil==2.8.2
python-dotenv==1.0.0
pytz==2023.3.post1
requests==2.31.0
six==1.16.0
stripe==7.12.0
typing_extensions==4.9.0
urllib3==2.1.0
yarl==1.9.4
```

New version where the bo wont start up
```
pip freeze
aiofiles==23.2.1
aiohttp==3.8.6
aiolimiter==1.1.0
aiomysql==0.2.0
aiosignal==1.3.1
aiosqlite==0.19.0
async-timeout==4.0.3
attrs==23.2.0
certifi==2023.11.17
cffi==1.16.0
charset-normalizer==3.3.2
colorama==0.4.6
cryptography==41.0.7
frozenlist==1.4.1
idna==3.6
iniconfig==2.0.0
multidict==6.0.4
mysql-connector-python==8.2.0
packaging==23.2
pluggy==1.3.0
protobuf==4.21.12
psutil==5.9.7
py-cord @ git+https://github.com/Pycord-Development/pycord@8a7ea476ac4c51bce80bfedb5c77739e51eeec6b
pycparser==2.21
PyMySQL==1.1.0
pytest==7.4.4
pytest-asyncio==0.23.3
python-dateutil==2.8.2
python-dotenv==1.0.0
pytz==2023.3.post1
requests==2.31.0
six==1.16.0
stripe==7.12.0
typing_extensions==4.9.0
urllib3==2.1.0
yarl==1.9.4
```

### Minimal Reproducible Code

```python
Install newest pycord version
```


### Expected Results

Bot start up normally

### Actual Results

```
python .
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "D:\Documents\bot discord\timed_role\__main__.py", line 7, in <module>
    from timeRoleBot import TimeRoleBot
  File "D:\Documents\bot discord\timed_role\timeRoleBot.py", line 16, in <module>
    from cogs.StatsCog import StatsCog
  File "D:\Documents\bot discord\timed_role\cogs\StatsCog.py", line 14, in <module>
    class StatsCog(Cog):
  File "D:\Documents\bot discord\timed_role\cogs\StatsCog.py", line 31, in StatsCog
    @stats.command(guild_ids=guildIds, description="Show the stats of the bot")
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\commands\core.py", line 1256, in wrap
    command = cls(func, parent=self, **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\commands\core.py", line 693, in __init__
    self._validate_parameters()
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\commands\core.py", line 711, in _validate_parameters       
    self.options = self._parse_options(params)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\commands\core.py", line 769, in _parse_options
    option = Option(option)
             ^^^^^^^^^^^^^^
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\commands\options.py", line 227, in __init__
    self.input_type = SlashCommandOptionType.from_datatype(input_type)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Documents\bot discord\timed_role\.venv\Lib\site-packages\discord\enums.py", line 802, in from_datatype
    if datatype.__name__ in ["Member", "User"]:
       ^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute '__name__'. Did you mean: '__ne__'?
```

StatsCog:
```
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from database.database import Database

from discord import ApplicationContext, Embed, default_permissions, slash_command
from discord.commands import SlashCommandGroup
from discord.ext.commands import Cog
from constant import guildIds, ADMIN_COMMANDS_SERVER
import psutil
from os.path import dirname, isabs, abspath


class StatsCog(Cog):
    
    stats = SlashCommandGroup("stats", "Commands related to stats")
    
    
    def __init__(self, database: Database) -> None:
        super().__init__()
        self.dabase = database
        
    def human_format(self, num: int) -> str:
        num = float('{:.3g}'.format(num))
        magnitude = 0
        while abs(num) >= 1000:
            magnitude += 1
            num /= 1000.0
        return '{} {}'.format('{:f}'.format(num).rstrip('0').rstrip('.'), ['', 'K', 'M', 'B', 'T'][magnitude])
    
    @stats.command(guild_ids=guildIds, description="Show the stats of the bot")      
    async def bot(self, ctx: ApplicationContext):
        await ctx.defer()
        embed = Embed(title=f"Stats of the bot")
        embed.add_field(name="Server count", value=f"{len(ctx.bot.guilds)} servers")
        member_cached_count = 0
        member_count = 0
        for guild in ctx.bot.guilds:
            member_cached_count += len(guild.members)
            member_count += guild.member_count
        embed.add_field(name="Member count cached", value=f"{self.human_format(member_cached_count)} users")
        embed.add_field(name="Member count", value=f"{self.human_format(member_count)} users")
        embed.add_field(name="Member fill cache", value=f"{round(member_cached_count/member_count * 100, 2)} %")
        embed.add_field(name="Latency", value=f"{ctx.bot.latency * 1000:.2f} ms to discord")
        embed.add_field(name="CPU", value=f"{psutil.cpu_percent()} % used")
        embed.add_field(name="RAM", value=f"{psutil.virtual_memory()[2]:.2f} % used")
        # disk_usage = psutil.disk_usage(dirname(self.database_path))
        # embed.add_field(name="DISK", value=f"{disk_usage.percent} % used")
        embed.set_footer(text="If you like the bot, please consider supporting it at: https://ko-fi.com/champymarty_botdev")
        await ctx.respond(embed=embed)
```

### Intents

moderation, members, guilds

### System Information

```
python -m discord -v
- Python v3.11.1-final
- py-cord v2.4.1-final
- aiohttp v3.8.6
- system info: Windows 10 10.0.22631
```

### Checklist

- [X] I have searched the open issues for duplicates.
- [X] I have shown the entire traceback, if possible.
- [X] I have removed my token from display, if visible.

### Additional Context

_No response_


Discussion:
I mean you're installing pycord from master, so breaking changes should be expected at any time.
Documentation follows when releases are pushed.


Yes but I asked in the help server and I was told there was no breaking changes and to raise a bug report

You've done the right thing, we will investigate.

Resolving patch:
diff --git a/discord/enums.py b/discord/enums.py
--- a/discord/enums.py
+++ b/discord/enums.py
@@ -800,9 +800,13 @@ def from_datatype(cls, datatype):
             # Type checking fails for this case, so ignore it.
             return cls.from_datatype(datatype.__args__)  # type: ignore
 
-        if datatype.__name__ in ["Member", "User"]:
+        if isinstance(datatype, str):
+            datatype_name = datatype
+        else:
+            datatype_name = datatype.__name__
+        if datatype_name in ["Member", "User"]:
             return cls.user
-        if datatype.__name__ in [
+        if datatype_name in [
             "GuildChannel",
             "TextChannel",
             "VoiceChannel",
@@ -814,14 +818,14 @@ def from_datatype(cls, datatype):
             "DMChannel",
         ]:
             return cls.channel
-        if datatype.__name__ == "Role":
+        if datatype_name == "Role":
             return cls.role
-        if datatype.__name__ == "Attachment":
+        if datatype_name == "Attachment":
             return cls.attachment
-        if datatype.__name__ == "Mentionable":
+        if datatype_name == "Mentionable":
             return cls.mentionable
 
-        if issubclass(datatype, str):
+        if isinstance(datatype, str) or issubclass(datatype, str):
             return cls.string
         if issubclass(datatype, bool):
             return cls.boolean


Test patch: