Browse Source

chore: fix errors

TextDisplay attribute error when doing TextDisplay.to_component_dict()
View.to_components() appending Item objects instead of Item.to_component_dict()
Changed View.to_components() sorting key
pull/10166/head
DA-344 5 months ago
parent
commit
0a0396889c
  1. 9
      discord/components.py
  2. 2
      discord/types/components.py
  3. 22
      discord/ui/container.py
  4. 38
      discord/ui/section.py
  5. 10
      discord/ui/text_display.py
  6. 38
      discord/ui/view.py

9
discord/components.py

@ -732,17 +732,13 @@ class SectionComponent(Component):
def __init__(self, data: SectionComponentPayload, state: Optional[ConnectionState]) -> None: def __init__(self, data: SectionComponentPayload, state: Optional[ConnectionState]) -> None:
self.components: List[SectionComponentType] = [] self.components: List[SectionComponentType] = []
self.accessory: Component = _component_factory(data['accessory'], state)
for component_data in data['components']: for component_data in data['components']:
component = _component_factory(component_data, state) component = _component_factory(component_data, state)
if component is not None: if component is not None:
self.components.append(component) # type: ignore # should be the correct type here self.components.append(component) # type: ignore # should be the correct type here
try:
self.accessory: Optional[Component] = _component_factory(data['accessory']) # type: ignore
except KeyError:
self.accessory = None
@property @property
def type(self) -> Literal[ComponentType.section]: def type(self) -> Literal[ComponentType.section]:
return ComponentType.section return ComponentType.section
@ -751,9 +747,8 @@ class SectionComponent(Component):
payload: SectionComponentPayload = { payload: SectionComponentPayload = {
'type': self.type.value, 'type': self.type.value,
'components': [c.to_dict() for c in self.components], 'components': [c.to_dict() for c in self.components],
'accessory': self.accessory.to_dict()
} }
if self.accessory:
payload['accessory'] = self.accessory.to_dict()
return payload return payload

2
discord/types/components.py

@ -128,7 +128,7 @@ class SelectMenu(SelectComponent):
class SectionComponent(ComponentBase): class SectionComponent(ComponentBase):
type: Literal[9] type: Literal[9]
components: List[Union[TextComponent, ButtonComponent]] components: List[Union[TextComponent, ButtonComponent]]
accessory: NotRequired[ComponentBase] accessory: ComponentBase
class TextComponent(ComponentBase): class TextComponent(ComponentBase):

22
discord/ui/container.py

@ -29,6 +29,7 @@ from .item import Item
from .view import View, _component_to_item from .view import View, _component_to_item
from .dynamic import DynamicItem from .dynamic import DynamicItem
from ..enums import ComponentType from ..enums import ComponentType
from ..utils import MISSING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Self from typing_extensions import Self
@ -61,13 +62,20 @@ class Container(View, Item[V]):
timeout: Optional[:class:`float`] timeout: Optional[:class:`float`]
Timeout in seconds from last interaction with the UI before no longer accepting input. Timeout in seconds from last interaction with the UI before no longer accepting input.
If ``None`` then there is no timeout. If ``None`` then there is no timeout.
row: Optional[:class:`int`]
The relative row this container belongs to. By default
items are arranged automatically into those rows. If you'd
like to control the relative positioning of the row then
passing an index is advised. For example, row=1 will show
up before row=2. Defaults to ``None``, which is automatic
ordering. The row number must be between 0 and 9 (i.e. zero indexed)
""" """
__discord_ui_container__ = True __discord_ui_container__ = True
def __init__( def __init__(
self, self,
children: List[Item[Any]], children: List[Item[Any]] = MISSING,
*, *,
accent_colour: Optional[Colour] = None, accent_colour: Optional[Colour] = None,
accent_color: Optional[Color] = None, accent_color: Optional[Color] = None,
@ -76,9 +84,10 @@ class Container(View, Item[V]):
row: Optional[int] = None, row: Optional[int] = None,
) -> None: ) -> None:
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
if len(children) + len(self._children) > 10: if children is not MISSING:
raise ValueError('maximum number of components exceeded') if len(children) + len(self._children) > 10:
self._children.extend(children) raise ValueError('maximum number of components exceeded')
self._children.extend(children)
self.spoiler: bool = spoiler self.spoiler: bool = spoiler
self._colour = accent_colour or accent_color self._colour = accent_colour or accent_color
@ -87,11 +96,6 @@ class Container(View, Item[V]):
self._rendered_row: Optional[int] = None self._rendered_row: Optional[int] = None
self.row: Optional[int] = row self.row: Optional[int] = row
def _init_children(self) -> List[Item[Self]]:
if self.__weights.max_weight != 10:
self.__weights.max_weight = 10
return super()._init_children()
@property @property
def children(self) -> List[Item[Self]]: def children(self) -> List[Item[Self]]:
"""List[:class:`Item`]: The children of this container.""" """List[:class:`Item`]: The children of this container."""

38
discord/ui/section.py

@ -28,6 +28,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, TypeVar, U
from .item import Item from .item import Item
from .text_display import TextDisplay from .text_display import TextDisplay
from ..enums import ComponentType from ..enums import ComponentType
from ..utils import MISSING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Self from typing_extensions import Self
@ -37,6 +38,8 @@ if TYPE_CHECKING:
V = TypeVar('V', bound='View', covariant=True) V = TypeVar('V', bound='View', covariant=True)
__all__ = ('Section',)
class Section(Item[V]): class Section(Item[V]):
"""Represents a UI section. """Represents a UI section.
@ -47,8 +50,8 @@ class Section(Item[V]):
---------- ----------
children: List[Union[:class:`str`, :class:`TextDisplay`]] children: List[Union[:class:`str`, :class:`TextDisplay`]]
The text displays of this section. Up to 3. The text displays of this section. Up to 3.
accessory: Optional[:class:`Item`] accessory: :class:`Item`
The section accessory. Defaults to ``None``. The section accessory.
row: Optional[:class:`int`] row: Optional[:class:`int`]
The relative row this section belongs to. By default The relative row this section belongs to. By default
items are arranged automatically into those rows. If you'd items are arranged automatically into those rows. If you'd
@ -65,16 +68,23 @@ class Section(Item[V]):
def __init__( def __init__(
self, self,
children: List[Union[Item[Any], str]], children: List[Union[Item[Any], str]] = MISSING,
*, *,
accessory: Optional[Item[Any]] = None, accessory: Item[Any],
row: Optional[int] = None, row: Optional[int] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
if len(children) > 3: self._children: List[Item[Any]] = []
raise ValueError('maximum number of children exceeded') if children is not MISSING:
self._children: List[Item[Any]] = [c if isinstance(c, Item) else TextDisplay(c) for c in children] if len(children) > 3:
self.accessory: Optional[Item[Any]] = accessory raise ValueError('maximum number of children exceeded')
self._children.extend(
[
c if isinstance(c, Item)
else TextDisplay(c) for c in children
],
)
self.accessory: Item[Any] = accessory
self.row = row self.row = row
@ -106,13 +116,14 @@ class Section(Item[V]):
Parameters Parameters
---------- ----------
item: Union[:class:`str`, :class:`TextDisplay`] item: Union[:class:`str`, :class:`Item`]
The text display to add. The items to append, if it is a string it automatically wrapped around
:class:`TextDisplay`.
Raises Raises
------ ------
TypeError TypeError
A :class:`TextDisplay` was not passed. An :class:`Item` or :class:`str` was not passed.
ValueError ValueError
Maximum number of children has been exceeded (3). Maximum number of children has been exceeded (3).
""" """
@ -161,14 +172,13 @@ class Section(Item[V]):
return cls( return cls(
children=[_component_to_item(c) for c in component.components], children=[_component_to_item(c) for c in component.components],
accessory=_component_to_item(component.accessory) if component.accessory else None, accessory=_component_to_item(component.accessory),
) )
def to_component_dict(self) -> Dict[str, Any]: def to_component_dict(self) -> Dict[str, Any]:
data = { data = {
'components': [c.to_component_dict() for c in self._children], 'components': [c.to_component_dict() for c in self._children],
'type': self.type.value, 'type': self.type.value,
'accessory': self.accessory.to_component_dict()
} }
if self.accessory:
data['accessory'] = self.accessory.to_component_dict()
return data return data

10
discord/ui/text_display.py

@ -60,14 +60,14 @@ class TextDisplay(Item[V]):
def __init__(self, content: str, *, row: Optional[int] = None) -> None: def __init__(self, content: str, *, row: Optional[int] = None) -> None:
super().__init__() super().__init__()
self.content: str = content self.content: str = content
self._underlying = TextDisplayComponent._raw_construct(
content=content,
)
self.row = row self.row = row
def to_component_dict(self): def to_component_dict(self):
return self._underlying.to_dict() return {
'type': self.type.value,
'content': self.content,
}
@property @property
def width(self): def width(self):
@ -75,7 +75,7 @@ class TextDisplay(Item[V]):
@property @property
def type(self) -> Literal[ComponentType.text_display]: def type(self) -> Literal[ComponentType.text_display]:
return self._underlying.type return ComponentType.text_display
def _is_v2(self) -> bool: def _is_v2(self) -> bool:
return True return True

38
discord/ui/view.py

@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable, ClassVar, Coroutine, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple, Type from typing import Any, Callable, ClassVar, Coroutine, Dict, Iterator, List, Optional, Sequence, TYPE_CHECKING, Tuple, Type, Union
from functools import partial from functools import partial
from itertools import groupby from itertools import groupby
@ -119,13 +119,11 @@ class _ViewWeights:
# fmt: off # fmt: off
__slots__ = ( __slots__ = (
'weights', 'weights',
'max_weight',
) )
# fmt: on # fmt: on
def __init__(self, children: List[Item]): def __init__(self, children: List[Item]):
self.weights: List[int] = [0, 0, 0, 0, 0] self.weights: List[int] = [0, 0, 0, 0, 0]
self.max_weight: int = 5
key = lambda i: sys.maxsize if i.row is None else i.row key = lambda i: sys.maxsize if i.row is None else i.row
children = sorted(children, key=key) children = sorted(children, key=key)
@ -146,8 +144,8 @@ class _ViewWeights:
self.weights.extend([0, 0, 0, 0, 0]) self.weights.extend([0, 0, 0, 0, 0])
if item.row is not None: if item.row is not None:
total = self.weights[item.row] + item.width total = self.weights[item.row] + item.width
if total > self.max_weight: if total > 5:
raise ValueError(f'item would not fit at row {item.row} ({total} > {self.max_weight} width)') raise ValueError(f'item would not fit at row {item.row} ({total} > 5 width)')
self.weights[item.row] = total self.weights[item.row] = total
item._rendered_row = item.row item._rendered_row = item.row
else: else:
@ -196,15 +194,15 @@ class View:
__discord_ui_view__: ClassVar[bool] = True __discord_ui_view__: ClassVar[bool] = True
__discord_ui_modal__: ClassVar[bool] = False __discord_ui_modal__: ClassVar[bool] = False
__discord_ui_container__: ClassVar[bool] = False __discord_ui_container__: ClassVar[bool] = False
__view_children_items__: ClassVar[List[ItemCallbackType[Any, Any]]] = [] __view_children_items__: ClassVar[List[Union[ItemCallbackType[Any, Any], Item[Any]]]] = []
def __init_subclass__(cls) -> None: def __init_subclass__(cls) -> None:
super().__init_subclass__() super().__init_subclass__()
children: Dict[str, ItemCallbackType[Any, Any]] = {} children: Dict[str, Union[ItemCallbackType[Any, Any], Item]] = {}
for base in reversed(cls.__mro__): for base in reversed(cls.__mro__):
for name, member in base.__dict__.items(): for name, member in base.__dict__.items():
if hasattr(member, '__discord_ui_model_type__'): if hasattr(member, '__discord_ui_model_type__') or isinstance(member, Item):
children[name] = member children[name] = member
if len(children) > 25: if len(children) > 25:
@ -214,12 +212,16 @@ class View:
def _init_children(self) -> List[Item[Self]]: def _init_children(self) -> List[Item[Self]]:
children = [] children = []
for func in self.__view_children_items__: for func in self.__view_children_items__:
item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__) if isinstance(func, Item):
item.callback = _ViewCallback(func, self, item) # type: ignore children.append(func)
item._view = self else:
setattr(self, func.__name__, item) item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
children.append(item) item.callback = _ViewCallback(func, self, item) # type: ignore
item._view = self
setattr(self, func.__name__, item)
children.append(item)
return children return children
def __init__(self, *, timeout: Optional[float] = 180.0): def __init__(self, *, timeout: Optional[float] = 180.0):
@ -275,7 +277,13 @@ class View:
# v2 components # v2 components
def key(item: Item) -> int: def key(item: Item) -> int:
return item._rendered_row or 0 if item._rendered_row is not None:
return item._rendered_row
try:
return self._children.index(item)
except ValueError:
return 0
# instead of grouping by row we will sort it so it is added # instead of grouping by row we will sort it so it is added
# in order and should work as the original implementation # in order and should work as the original implementation
@ -290,7 +298,7 @@ class View:
index = rows_index.get(row) index = rows_index.get(row)
if index is not None: if index is not None:
components[index]['components'].append(child) components[index]['components'].append(child.to_component_dict())
else: else:
components.append( components.append(
{ {

Loading…
Cancel
Save