From 9663af13598ff4e38e13a4fe74f311d9d214e7b3 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 23 Oct 2025 10:27:04 -0700 Subject: [PATCH 1/7] feat: rework how item.view is determined --- discord/ui/action_row.py | 2 +- discord/ui/container.py | 6 ++---- discord/ui/item.py | 15 ++++++++++++--- discord/ui/section.py | 11 ++--------- discord/ui/view.py | 10 ++++++---- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index d3607867b7..3034ec7acd 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -140,7 +140,6 @@ def add_item(self, item: ViewItem) -> Self: if self.width + item.width > 5: raise ValueError(f"Not enough space left on this ActionRow") - item._view = self.view item.parent = self self.children.append(item) @@ -162,6 +161,7 @@ def remove_item(self, item: ViewItem | str | int) -> Self: self.children.remove(item) except ValueError: pass + item.parent = None return self def get_item(self, id: str | int) -> ViewItem | None: diff --git a/discord/ui/container.py b/discord/ui/container.py index f676f4b086..c3f8c5736c 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -149,9 +149,6 @@ def add_item(self, item: ViewItem) -> Self: f"{item.__class__!r} cannot be added directly. Use ActionRow instead." ) - item._view = self.view - if hasattr(item, "items"): - item.view = self item.parent = self self.items.append(item) @@ -170,12 +167,13 @@ def remove_item(self, item: ViewItem | str | int) -> Self: if isinstance(item, (str, int)): item = self.get_item(item) try: - if isinstance(item, Container): + if item.parent is self: self.items.remove(item) else: item.parent.remove_item(item) except ValueError: pass + item.parent = None return self def get_item(self, id: str | int) -> ViewItem | None: diff --git a/discord/ui/item.py b/discord/ui/item.py index 958f60a7a7..938af42752 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -35,6 +35,8 @@ "ModalItem", ) +from ..utils import deprecated, warn_deprecated + if TYPE_CHECKING: from ..components import Component from ..enums import ComponentType @@ -152,7 +154,7 @@ def __init__(self): self._view: V | None = None self._row: int | None = None self._rendered_row: int | None = None - self.parent: ViewItem | BaseView | None = self.view + self.parent: ViewItem | BaseView | None = None @property def row(self) -> int | None: @@ -208,10 +210,17 @@ def view(self) -> V | None: Optional[:class:`BaseView`] The parent view of this item, or ``None`` if the item is not attached to any view. """ - return self._view + if self._view: + return self._view + if self.parent: + if isinstance(self.parent, BaseView): + return self.parent + return self.parent.view + return None @view.setter - def view(self, value) -> None: + def view(self, value: V | None) -> None: + warn_deprecated("Manually setting .view", since="2.7", removed="2.8") self._view = value async def callback(self, interaction: Interaction): diff --git a/discord/ui/section.py b/discord/ui/section.py index 79d092595c..069b600ea5 100644 --- a/discord/ui/section.py +++ b/discord/ui/section.py @@ -162,6 +162,7 @@ def remove_item(self, item: ViewItem | str | int) -> Self: self.items.remove(item) except ValueError: pass + item.parent = None return self def get_item(self, id: int | str) -> ViewItem | None: @@ -226,8 +227,7 @@ def set_accessory(self, item: ViewItem) -> Self: if not isinstance(item, ViewItem): raise TypeError(f"expected ViewItem not {item.__class__!r}") - if self.view: - item._view = self.view + item.parent = self self.accessory = item @@ -260,13 +260,6 @@ def set_thumbnail( return self.set_accessory(thumbnail) - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.walk_items(): - item._view = value - item.parent = self - def copy_text(self) -> str: """Returns the text of all :class:`~discord.ui.TextDisplay` items in this section. Equivalent to the `Copy Text` option on Discord clients. diff --git a/discord/ui/view.py b/discord/ui/view.py index 9d03339d09..49f7ebd70b 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -235,11 +235,10 @@ def add_item(self, item: ViewItem[V]) -> Self: raise TypeError(f"expected ViewItem not {item.__class__!r}") item.parent = self - item._view = self self.children.append(item) return self - def remove_item(self, item: ViewItem[V] | int | str) -> None: + def remove_item(self, item: ViewItem[V] | int | str) -> Self: """Removes an item from the view. If an :class:`int` or :class:`str` is passed, the item will be removed by ViewItem ``id`` or ``custom_id`` respectively. @@ -252,17 +251,20 @@ def remove_item(self, item: ViewItem[V] | int | str) -> None: if isinstance(item, (str, int)): item = self.get_item(item) try: - if isinstance(item.parent, BaseView): + if item.parent is self: self.children.remove(item) else: item.parent.remove_item(item) except ValueError: pass + item.parent = None return self - def clear_items(self) -> None: + def clear_items(self) -> Self: """Removes all items from this view.""" self.children.clear() + for child in self.children: + child.parent = None return self async def interaction_check(self, interaction: Interaction) -> bool: From 8455d01fac80df1fca3de9b642a18894e88f2a2c Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 23 Oct 2025 10:31:47 -0700 Subject: [PATCH 2/7] docs: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 649b6c1901..22b83da996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,12 @@ These changes are available on the `master` branch, but have not yet been releas ### Changed ### Fixed +- Fixed the `view` attribute on many view items being incorrect. + ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) ### Deprecated +- Deprecated manually setting the `view` attribute on view items. + ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) ### Removed From c3f001023285dc7c2356bda25f450407ddb01c49 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:32:20 +0000 Subject: [PATCH 3/7] style(pre-commit): auto fixes from pre-commit.com hooks --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b83da996..35fd914feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,10 +15,12 @@ These changes are available on the `master` branch, but have not yet been releas ### Changed ### Fixed + - Fixed the `view` attribute on many view items being incorrect. ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) ### Deprecated + - Deprecated manually setting the `view` attribute on view items. ([#2981](https://github.com/Pycord-Development/pycord/pull/2981)) From a7b56b5322344d3bdc6e64a193e191780c275712 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 23 Oct 2025 10:32:29 -0700 Subject: [PATCH 4/7] Update discord/ui/view.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: plun1331 --- discord/ui/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ui/view.py b/discord/ui/view.py index 49f7ebd70b..180a267146 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -262,9 +262,9 @@ def remove_item(self, item: ViewItem[V] | int | str) -> Self: def clear_items(self) -> Self: """Removes all items from this view.""" - self.children.clear() for child in self.children: child.parent = None + self.children.clear() return self async def interaction_check(self, interaction: Interaction) -> bool: From 24bb592569def53ed85b0699f3e4861f3483ef94 Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 23 Oct 2025 11:12:14 -0700 Subject: [PATCH 5/7] remove useless stuff --- discord/ui/action_row.py | 7 ------- discord/ui/container.py | 9 --------- discord/ui/view.py | 3 --- 3 files changed, 19 deletions(-) diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index 3034ec7acd..6737a1625b 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -351,13 +351,6 @@ def add_select( return self.add_item(select) - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.children: - item.parent = self - item._view = value - def is_dispatchable(self) -> bool: return any(item.is_dispatchable() for item in self.children) diff --git a/discord/ui/container.py b/discord/ui/container.py index c3f8c5736c..4c738c8fdf 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -359,15 +359,6 @@ def colour(self, value: int | Colour | None): # type: ignore color = colour - @ViewItem.view.setter - def view(self, value): - self._view = value - for item in self.items: - item.parent = self - item._view = value - if hasattr(item, "items") or hasattr(item, "children"): - item.view = value - def is_dispatchable(self) -> bool: return any(item.is_dispatchable() for item in self.items) diff --git a/discord/ui/view.py b/discord/ui/view.py index 49f7ebd70b..a8499e0ef3 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -577,7 +577,6 @@ def __init__( **func.__discord_ui_model_kwargs__ ) item.callback = partial(func, self, item) - item._view = self item.parent = self setattr(self, func.__name__, item) self.children.append(item) @@ -884,8 +883,6 @@ def add_item(self, item: ViewItem[V]) -> Self: ) super().add_item(item) - if hasattr(item, "items"): - item.view = self return self def refresh(self, components: list[Component]): From d8be20f86480a19483ce2b820138824e52884d9f Mon Sep 17 00:00:00 2001 From: plun1331 Date: Thu, 23 Oct 2025 11:17:21 -0700 Subject: [PATCH 6/7] fuckin circles man --- discord/ui/item.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discord/ui/item.py b/discord/ui/item.py index 938af42752..1a35281c49 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -35,7 +35,7 @@ "ModalItem", ) -from ..utils import deprecated, warn_deprecated +from ..utils import warn_deprecated if TYPE_CHECKING: from ..components import Component @@ -213,6 +213,7 @@ def view(self) -> V | None: if self._view: return self._view if self.parent: + from .view import BaseView if isinstance(self.parent, BaseView): return self.parent return self.parent.view From 9f9c0215ee8e3f5cc8d6f1d2827de149d7d610f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 18:17:52 +0000 Subject: [PATCH 7/7] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/ui/item.py | 1 + 1 file changed, 1 insertion(+) diff --git a/discord/ui/item.py b/discord/ui/item.py index 1a35281c49..8f86566241 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -214,6 +214,7 @@ def view(self) -> V | None: return self._view if self.parent: from .view import BaseView + if isinstance(self.parent, BaseView): return self.parent return self.parent.view