Skip to content
This repository was archived by the owner on Aug 28, 2019. It is now read-only.

Commit 1458251

Browse files
committed
Change View.children to be a property
This allows users to call remove_item in a loop. Likewise, it prevents the footgun of doing children.append(...) which does not uphold the invariants with the weight system.
1 parent d5d9a53 commit 1458251

File tree

2 files changed

+19
-21
lines changed

2 files changed

+19
-21
lines changed

discord/ui/modal.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ async def on_submit(self, interaction: discord.Interaction):
8989
------------
9090
title: :class:`str`
9191
The title of the modal.
92-
children: List[:class:`Item`]
93-
The list of children attached to this view.
9492
custom_id: :class:`str`
9593
The ID of the modal that gets received during an interaction.
9694
"""
@@ -172,7 +170,7 @@ def _refresh(self, components: Sequence[ModalSubmitComponentInteractionDataPaylo
172170
if component['type'] == 1:
173171
self._refresh(component['components'])
174172
else:
175-
item = find(lambda i: i.custom_id == component['custom_id'], self.children) # type: ignore
173+
item = find(lambda i: i.custom_id == component['custom_id'], self._children) # type: ignore
176174
if item is None:
177175
_log.debug("Modal interaction referencing unknown item custom_id %s. Discarding", component['custom_id'])
178176
continue

discord/ui/view.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,6 @@ class View:
151151
timeout: Optional[:class:`float`]
152152
Timeout in seconds from last interaction with the UI before no longer accepting input.
153153
If ``None`` then there is no timeout.
154-
155-
Attributes
156-
------------
157-
children: List[:class:`Item`]
158-
The list of children attached to this view.
159154
"""
160155

161156
__discord_ui_view__: ClassVar[bool] = True
@@ -186,16 +181,16 @@ def _init_children(self) -> List[Item[Self]]:
186181

187182
def __init__(self, *, timeout: Optional[float] = 180.0):
188183
self.__timeout = timeout
189-
self.children: List[Item[Self]] = self._init_children()
190-
self.__weights = _ViewWeights(self.children)
184+
self._children: List[Item[Self]] = self._init_children()
185+
self.__weights = _ViewWeights(self._children)
191186
self.id: str = os.urandom(16).hex()
192187
self.__cancel_callback: Optional[Callable[[View], None]] = None
193188
self.__timeout_expiry: Optional[float] = None
194189
self.__timeout_task: Optional[asyncio.Task[None]] = None
195190
self.__stopped: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
196191

197192
def __repr__(self) -> str:
198-
return f'<{self.__class__.__name__} timeout={self.timeout} children={len(self.children)}>'
193+
return f'<{self.__class__.__name__} timeout={self.timeout} children={len(self._children)}>'
199194

200195
async def __timeout_task_impl(self) -> None:
201196
while True:
@@ -218,7 +213,7 @@ def to_components(self) -> List[Dict[str, Any]]:
218213
def key(item: Item) -> int:
219214
return item._rendered_row or 0
220215

221-
children = sorted(self.children, key=key)
216+
children = sorted(self._children, key=key)
222217
components: List[Dict[str, Any]] = []
223218
for _, group in groupby(children, key=key):
224219
children = [item.to_component_dict() for item in group]
@@ -257,6 +252,11 @@ def timeout(self, value: Optional[float]) -> None:
257252

258253
self.__timeout = value
259254

255+
@property
256+
def children(self) -> List[Item[Self]]:
257+
"""List[:class:`Item`]: The list of children attached to this view."""
258+
return self._children.copy()
259+
260260
@classmethod
261261
def from_message(cls, message: Message, /, *, timeout: Optional[float] = 180.0) -> View:
262262
"""Converts a message's components into a :class:`View`.
@@ -304,7 +304,7 @@ def add_item(self, item: Item[Any]) -> Self:
304304
or the row the item is trying to be added to is full.
305305
"""
306306

307-
if len(self.children) > 25:
307+
if len(self._children) > 25:
308308
raise ValueError('maximum number of children exceeded')
309309

310310
if not isinstance(item, Item):
@@ -313,7 +313,7 @@ def add_item(self, item: Item[Any]) -> Self:
313313
self.__weights.add_item(item)
314314

315315
item._view = self
316-
self.children.append(item)
316+
self._children.append(item)
317317
return self
318318

319319
def remove_item(self, item: Item[Any]) -> Self:
@@ -329,7 +329,7 @@ def remove_item(self, item: Item[Any]) -> Self:
329329
"""
330330

331331
try:
332-
self.children.remove(item)
332+
self._children.remove(item)
333333
except ValueError:
334334
pass
335335
else:
@@ -342,7 +342,7 @@ def clear_items(self) -> Self:
342342
This function returns the class instance to allow for fluent-style
343343
chaining.
344344
"""
345-
self.children.clear()
345+
self._children.clear()
346346
self.__weights.clear()
347347
return self
348348

@@ -445,7 +445,7 @@ def _refresh(self, components: List[Component]) -> None:
445445
# fmt: off
446446
old_state: Dict[Tuple[int, str], Item[Any]] = {
447447
(item.type.value, item.custom_id): item # type: ignore
448-
for item in self.children
448+
for item in self._children
449449
if item.is_dispatchable()
450450
}
451451
# fmt: on
@@ -459,7 +459,7 @@ def _refresh(self, components: List[Component]) -> None:
459459
older._refresh_component(component)
460460
children.append(older)
461461

462-
self.children = children
462+
self._children = children
463463

464464
def stop(self) -> None:
465465
"""Stops listening to interaction events from this view.
@@ -492,7 +492,7 @@ def is_persistent(self) -> bool:
492492
A persistent view has all their components with a set ``custom_id`` and
493493
a :attr:`timeout` set to ``None``.
494494
"""
495-
return self.timeout is None and all(item.is_persistent() for item in self.children)
495+
return self.timeout is None and all(item.is_persistent() for item in self._children)
496496

497497
async def wait(self) -> bool:
498498
"""Waits until the view has finished interacting.
@@ -547,7 +547,7 @@ def add_view(self, view: View, message_id: Optional[int] = None) -> None:
547547

548548
self.__verify_integrity()
549549

550-
for item in view.children:
550+
for item in view._children:
551551
if item.is_dispatchable():
552552
self._views[(item.type.value, message_id, item.custom_id)] = (view, item) # type: ignore
553553

@@ -559,7 +559,7 @@ def remove_view(self, view: View) -> None:
559559
self._modals.pop(view.custom_id, None) # type: ignore
560560
return
561561

562-
for item in view.children:
562+
for item in view._children:
563563
if item.is_dispatchable():
564564
self._views.pop((item.type.value, item.custom_id), None) # type: ignore
565565

0 commit comments

Comments
 (0)