diff --git a/discord/app_commands/commands.py b/discord/app_commands/commands.py index fd6388443..8e6693408 100644 --- a/discord/app_commands/commands.py +++ b/discord/app_commands/commands.py @@ -46,6 +46,7 @@ from typing import ( ) import re +from copy import copy as shallow_copy from ..enums import AppCommandOptionType, AppCommandType, ChannelType, Locale from .models import Choice @@ -707,25 +708,10 @@ class Command(Generic[GroupT, P, T]): ) -> Command: bindings = {} if bindings is MISSING else bindings - cls = self.__class__ - copy = cls.__new__(cls) - copy.name = self.name - copy._locale_name = self._locale_name - copy._guild_ids = self._guild_ids - copy.checks = self.checks - copy.description = self.description - copy._locale_description = self._locale_description - copy.default_permissions = self.default_permissions - copy.guild_only = self.guild_only - copy.nsfw = self.nsfw - copy._attr = self._attr - copy._callback = self._callback - copy.on_error = self.on_error + copy = shallow_copy(self) copy._params = self._params.copy() - copy.module = self.module copy.parent = parent copy.binding = bindings.get(self.binding) if self.binding is not None else binding - copy.extras = self.extras if copy._attr and set_on_binding: setattr(copy.binding, copy._attr, copy) @@ -1622,22 +1608,9 @@ class Group: ) -> Group: bindings = {} if bindings is MISSING else bindings - cls = self.__class__ - copy = cls.__new__(cls) - copy.name = self.name - copy._locale_name = self._locale_name - copy._guild_ids = self._guild_ids - copy.description = self.description - copy._locale_description = self._locale_description + copy = shallow_copy(self) copy.parent = parent - copy.module = self.module - copy.default_permissions = self.default_permissions - copy.guild_only = self.guild_only - copy.nsfw = self.nsfw - copy._attr = self._attr - copy._owner_cls = self._owner_cls copy._children = {} - copy.extras = self.extras bindings[self] = copy diff --git a/tests/test_app_commands_group.py b/tests/test_app_commands_group.py index 756f7c48f..d5f07976f 100644 --- a/tests/test_app_commands_group.py +++ b/tests/test_app_commands_group.py @@ -354,3 +354,46 @@ def test_cog_group_with_subclassed_subclass_group(): assert cog.sub_group.my_command.parent is cog.sub_group assert cog.my_cog_command.parent is cog.sub_group assert cog.my_cog_command.binding is cog + + +def test_cog_group_with_custom_state_issue9383(): + class InnerGroup(app_commands.Group): + def __init__(self): + super().__init__() + self.state: int = 20 + + @app_commands.command() + async def my_command(self, interaction: discord.Interaction) -> None: + ... + + class MyCog(commands.GroupCog): + inner = InnerGroup() + + @app_commands.command() + async def my_regular_command(self, interaction: discord.Interaction) -> None: + ... + + @inner.command() + async def my_inner_command(self, interaction: discord.Interaction) -> None: + ... + + cog = MyCog() + assert cog.inner.state == 20 + assert cog.my_regular_command is not MyCog.my_regular_command + + # Basically the same tests as above... (superset?) + assert MyCog.__cog_app_commands__[0].parent is not cog + assert MyCog.__cog_app_commands__[0].parent is not cog.__cog_app_commands_group__ + assert InnerGroup.__discord_app_commands_group_children__[0].parent is not cog.inner + assert InnerGroup.__discord_app_commands_group_children__[0].parent is not cog.inner + assert cog.inner is not MyCog.inner + assert cog.inner.my_command is not InnerGroup.my_command + assert cog.inner.my_command is not InnerGroup.my_command + assert cog.my_inner_command is not MyCog.my_inner_command + assert not hasattr(cog.inner, 'my_inner_command') + assert cog.__cog_app_commands_group__ is not None + assert cog.__cog_app_commands_group__.parent is None + assert cog.inner.parent is cog.__cog_app_commands_group__ + assert cog.inner.my_command.parent is cog.inner + assert cog.my_inner_command.parent is cog.inner + assert cog.my_inner_command.binding is cog