From 3a9758595d14a8513b6f20a8b5fbd7a2ed6f3964 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:25:39 -0400 Subject: [PATCH 1/2] feat: allow list operations on ContainerComponent Ideally improves UX. Hasn't been tested much. --- interactions/models/discord/components.py | 92 +++++++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index d0b08bf97..0cf88efd2 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -1,7 +1,9 @@ +from collections import UserList import contextlib import uuid from abc import abstractmethod -from typing import Any, Dict, Iterator, List, Optional, Sequence, Union, TYPE_CHECKING +from typing import Any, Dict, Iterator, List, Optional, Sequence, Union, TYPE_CHECKING, overload +from typing_extensions import Self import attrs import discord_typings @@ -1092,7 +1094,12 @@ def to_dict(self) -> dict: } -class ContainerComponent(BaseComponent): +class ContainerComponent( + BaseComponent, + UserList[ + ActionRow | SectionComponent | TextDisplayComponent | MediaGalleryComponent | FileComponent | SeparatorComponent + ], +): """ A top-level layout component. Containers are visually distinct from surrounding components and have an optional customizable color bar. @@ -1103,10 +1110,6 @@ class ContainerComponent(BaseComponent): type Union[ComponentType, int]: The type of component, as defined by discord. This cannot be modified. """ - - components: list[ - ActionRow | SectionComponent | TextDisplayComponent | MediaGalleryComponent | FileComponent | SeparatorComponent - ] accent_color: Optional[int] = None spoiler: bool = False @@ -1121,13 +1124,86 @@ def __init__( accent_color: Optional[int] = None, spoiler: bool = False, ): - self.components = list(components) + self.data = list(components) self.accent_color = accent_color self.spoiler = spoiler self.type = ComponentType.CONTAINER + @property + def components( + self, + ) -> list[ + ActionRow | SectionComponent | TextDisplayComponent | MediaGalleryComponent | FileComponent | SeparatorComponent + ]: + return self.data + + @components.setter + def components( + self, + value: list[ + ActionRow + | SectionComponent + | TextDisplayComponent + | MediaGalleryComponent + | FileComponent + | SeparatorComponent + ], + ) -> None: + self.data = value + + @overload + def __getitem__( + self, i: int + ) -> ( + ActionRow | SectionComponent | TextDisplayComponent | MediaGalleryComponent | FileComponent | SeparatorComponent + ): ... + + @overload + def __getitem__(self, i: slice) -> Self: ... + + def __getitem__( + self, i: int | slice + ) -> ( + Self + | ActionRow + | SectionComponent + | TextDisplayComponent + | MediaGalleryComponent + | FileComponent + | SeparatorComponent + ): + if isinstance(i, slice): + return self.__class__(*self.data[i], accent_color=self.accent_color, spoiler=self.spoiler) + return self.data[i] + + def __add__(self, other: Any) -> Self: + if isinstance(other, ContainerComponent): + return self.__class__(*(self.data + other.data), accent_color=self.accent_color, spoiler=self.spoiler) + if isinstance(other, UserList): + return self.__class__(*(self.data + other.data), accent_color=self.accent_color, spoiler=self.spoiler) + if isinstance(other, type(self.data)): + return self.__class__(*(self.data + other), accent_color=self.accent_color, spoiler=self.spoiler) + return self.__class__(*(self.data + list(other)), accent_color=self.accent_color, spoiler=self.spoiler) + + def __radd__(self, other: Any) -> Self: + if isinstance(other, ContainerComponent): + return self.__class__(*(self.data + other.data), accent_color=other.accent_color, spoiler=other.spoiler) + if isinstance(other, UserList): + return self.__class__(*(other.data + self.data), accent_color=self.accent_color, spoiler=self.spoiler) + if isinstance(other, type(self.data)): + return self.__class__(*(other + self.data), accent_color=self.accent_color, spoiler=self.spoiler) + return self.__class__(*(list(other) + self.data), accent_color=self.accent_color, spoiler=self.spoiler) + + def __mul__(self, n: int) -> Self: + return self.__class__(*(self.data * n), accent_color=self.accent_color, spoiler=self.spoiler) + + __rmul__ = __mul__ + + def copy(self) -> Self: + return self.__class__(*self.data, accent_color=self.accent_color, spoiler=self.spoiler) + @classmethod - def from_dict(cls, data: dict) -> "ContainerComponent": + def from_dict(cls, data: dict) -> Self: return cls( *[BaseComponent.from_dict_factory(component) for component in data["components"]], accent_color=data.get("accent_color"), From 01beb94306e741e16b933571b1a46b24720986e0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:41:01 +0000 Subject: [PATCH 2/2] ci: correct from checks. --- interactions/models/discord/components.py | 1 + 1 file changed, 1 insertion(+) diff --git a/interactions/models/discord/components.py b/interactions/models/discord/components.py index 0cf88efd2..4c129a0fc 100644 --- a/interactions/models/discord/components.py +++ b/interactions/models/discord/components.py @@ -1110,6 +1110,7 @@ class ContainerComponent( type Union[ComponentType, int]: The type of component, as defined by discord. This cannot be modified. """ + accent_color: Optional[int] = None spoiler: bool = False