pycascadeui 3.12.2__tar.gz → 3.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pycascadeui-3.12.2/pycascadeui.egg-info → pycascadeui-3.13.0}/PKG-INFO +3 -3
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/__init__.py +1 -1
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/base.py +10 -8
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/inputs.py +8 -11
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/devtools.py +20 -1
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/manager.py +359 -20
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/actions.py +9 -2
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/middleware/persistence.py +43 -18
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/reducers.py +7 -1
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/store.py +161 -9
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/responses.py +22 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/_interaction.py +10 -8
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/_navigation.py +27 -4
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/base.py +283 -68
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/form.py +4 -4
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/persistent.py +128 -37
- {pycascadeui-3.12.2 → pycascadeui-3.13.0/pycascadeui.egg-info}/PKG-INFO +3 -3
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/pycascadeui.egg-info/requires.txt +2 -2
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/pyproject.toml +3 -3
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_devtools.py +59 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_dynamic_persistent_button.py +12 -2
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_ephemeral_refresh.py +58 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_form_patterns.py +13 -4
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_inputs.py +14 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_layout_view.py +191 -1
- pycascadeui-3.13.0/tests/test_message_cleanup.py +521 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_navigation.py +57 -2
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_persistence.py +1350 -2
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_persistence_middleware.py +3 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_persistent_views.py +421 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_public_api.py +10 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_state_store.py +217 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_view_init.py +25 -0
- pycascadeui-3.12.2/tests/test_message_cleanup.py +0 -239
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/LICENSE +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/README.md +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/__main__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/buttons.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/patterns/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/patterns/v1.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/patterns/v2.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/selects.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/types.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/v1_composition.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/components/wrappers.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/exceptions.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/backends/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/backends/memory.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/backends/postgres.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/backends/sqlite.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/config.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/migrations.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/protocols.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/schema.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/persistence/schema_postgres.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/py.typed +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/setup.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/_batching.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/computed.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/middleware/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/middleware/logging.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/middleware/undo.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/singleton.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/slots.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/state/types.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/testing.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/theming/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/theming/context.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/theming/core.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/theming/themes.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/tracing.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/coercion.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/decorators.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/errors.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/fetch.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/guards.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/hooks.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/logging.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/strings.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/utils/tasks.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/validation.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/_placement.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/layout.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/__init__.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/leaderboard.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/menu.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/paginated.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/roles.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/tabs.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/types.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/patterns/wizard.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/cascadeui/views/view.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/pycascadeui.egg-info/SOURCES.txt +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/pycascadeui.egg-info/dependency_links.txt +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/pycascadeui.egg-info/top_level.txt +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/setup.cfg +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_auto_defer.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_backends.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_backends_postgres.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_backends_raw_sql.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_batching.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_button_grid.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_components.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_computed.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_emoji_grid.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_examples.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_fetch.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_five_pillars.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_hooks.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_input_coercion.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_instance_limit.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_invariants.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_layout_pagination.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_layout_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_layout_persistent.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_leaderboard_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_logging_middleware.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_menu_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_middleware.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_nav_version.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_navigation_artifact_restore.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_navigation_matrix.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_owner_only.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_paginated_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_pagination.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_pillar_isolation.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_placement.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_rebuild_callback.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_reducers.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_roles_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_scoping.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_selects.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_state_slots.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_tab_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_task_manager.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_theming.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_undo.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_update_coalescing.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_v2_helpers.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_validation.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_wizard_patterns.py +0 -0
- {pycascadeui-3.12.2 → pycascadeui-3.13.0}/tests/test_wrappers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycascadeui
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.13.0
|
|
4
4
|
Summary: Redux-inspired UI framework for discord.py
|
|
5
5
|
Author-email: HollowTheSilver <hollow@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,8 +28,8 @@ Requires-Dist: discord.py>=2.7
|
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
30
|
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
31
|
-
Requires-Dist: black
|
|
32
|
-
Requires-Dist: isort
|
|
31
|
+
Requires-Dist: black<27.0,>=26.3; extra == "dev"
|
|
32
|
+
Requires-Dist: isort<9.0,>=8.0; extra == "dev"
|
|
33
33
|
Requires-Dist: testcontainers[postgres]>=4.0; extra == "dev"
|
|
34
34
|
Provides-Extra: docs
|
|
35
35
|
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
@@ -184,7 +184,7 @@ if "PostgresBackend" in _persistence_all:
|
|
|
184
184
|
# // ========================================( Script )======================================== // #
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
__version__ = "3.
|
|
187
|
+
__version__ = "3.13.0"
|
|
188
188
|
|
|
189
189
|
# Export public API
|
|
190
190
|
__all__ = [
|
|
@@ -13,7 +13,13 @@ from ..state.actions import ActionCreators
|
|
|
13
13
|
from ..state.store import _CURRENT_INTERACTION
|
|
14
14
|
from ..utils.coercion import coerce_snowflake_match
|
|
15
15
|
from ..utils.hooks import accepts_second_positional, await_maybe, can_accept_positional
|
|
16
|
-
from ..utils.responses import
|
|
16
|
+
from ..utils.responses import (
|
|
17
|
+
ack_backstop,
|
|
18
|
+
open_modal_safe,
|
|
19
|
+
respond_safe,
|
|
20
|
+
trailing_ack,
|
|
21
|
+
validate_ack_delay,
|
|
22
|
+
)
|
|
17
23
|
from .types import MAX_SELECT_OPTIONS
|
|
18
24
|
|
|
19
25
|
logger = logging.getLogger(__name__)
|
|
@@ -552,15 +558,11 @@ class DynamicPersistentButton(
|
|
|
552
558
|
``DynamicPersistentButton`` itself, because a class's
|
|
553
559
|
``__init_subclass__`` runs on its subclasses, not on itself.
|
|
554
560
|
|
|
555
|
-
Also validates ``auto_defer_delay`` (
|
|
556
|
-
definition time,
|
|
561
|
+
Also validates ``auto_defer_delay`` (a positive number under Discord's
|
|
562
|
+
3s ack deadline) at definition time, sharing the check ``Modal`` uses.
|
|
557
563
|
"""
|
|
558
564
|
super().__init_subclass__(**kwargs)
|
|
559
|
-
|
|
560
|
-
if not isinstance(delay, (int, float)) or isinstance(delay, bool) or delay <= 0:
|
|
561
|
-
raise ValueError(
|
|
562
|
-
f"{cls.__name__}.auto_defer_delay must be a positive number, got {delay!r}"
|
|
563
|
-
)
|
|
565
|
+
validate_ack_delay(cls.__name__, "auto_defer_delay", cls.auto_defer_delay)
|
|
564
566
|
_dynamic_button_classes[f"{cls.__module__}.{cls.__qualname__}"] = cls
|
|
565
567
|
|
|
566
568
|
@classmethod
|
|
@@ -11,7 +11,7 @@ from discord import CheckboxGroupOption, Interaction, RadioGroupOption, TextStyl
|
|
|
11
11
|
from discord.ui.select import BaseSelect
|
|
12
12
|
|
|
13
13
|
from ..utils.hooks import await_maybe
|
|
14
|
-
from ..utils.responses import ack_backstop, respond_safe, trailing_ack
|
|
14
|
+
from ..utils.responses import ack_backstop, respond_safe, trailing_ack, validate_ack_delay
|
|
15
15
|
from ..utils.strings import slugify
|
|
16
16
|
from ..validation import validate_fields
|
|
17
17
|
from .base import StatefulComponent, require_value_callback
|
|
@@ -593,19 +593,16 @@ class Modal(discord.ui.Modal, StatefulComponent):
|
|
|
593
593
|
auto_defer_delay: float = 2.5
|
|
594
594
|
|
|
595
595
|
def __init_subclass__(cls, **kwargs):
|
|
596
|
-
"""Reject
|
|
596
|
+
"""Reject an unusable ``auto_defer_delay`` at subclass-definition time.
|
|
597
597
|
|
|
598
|
-
A value at or below zero
|
|
599
|
-
|
|
600
|
-
dropped interaction at runtime.
|
|
601
|
-
|
|
598
|
+
A value at or below zero, the wrong type, or one at or past Discord's
|
|
599
|
+
3s ack deadline silently defeats the ack backstop, so it fails when the
|
|
600
|
+
subclass is defined rather than as a dropped interaction at runtime.
|
|
601
|
+
Shares :func:`~cascadeui.utils.responses.validate_ack_delay` with the
|
|
602
|
+
view side's ``_ACK_DELAY_ATTRS`` check.
|
|
602
603
|
"""
|
|
603
604
|
super().__init_subclass__(**kwargs)
|
|
604
|
-
|
|
605
|
-
if not isinstance(delay, (int, float)) or isinstance(delay, bool) or delay <= 0:
|
|
606
|
-
raise ValueError(
|
|
607
|
-
f"{cls.__name__}.auto_defer_delay must be a positive number, got {delay!r}"
|
|
608
|
-
)
|
|
605
|
+
validate_ack_delay(cls.__name__, "auto_defer_delay", cls.auto_defer_delay)
|
|
609
606
|
|
|
610
607
|
def __init__(
|
|
611
608
|
self,
|
|
@@ -563,6 +563,11 @@ class InspectorView(TabLayoutView):
|
|
|
563
563
|
|
|
564
564
|
# Exit live instances first
|
|
565
565
|
for view_id, view in list(active.items()):
|
|
566
|
+
# One view's exit cascades to the children attached to it, so a
|
|
567
|
+
# snapshot taken before the first exit can name a view that is
|
|
568
|
+
# already gone by the time the loop reaches it.
|
|
569
|
+
if view._torn_down():
|
|
570
|
+
continue
|
|
566
571
|
try:
|
|
567
572
|
await view.exit()
|
|
568
573
|
exited += 1
|
|
@@ -1429,6 +1434,9 @@ class DevToolsCog(commands.Cog, name="cascadeui_devtools"):
|
|
|
1429
1434
|
for view_id, view in list(active.items()):
|
|
1430
1435
|
if gid is not None and getattr(view, "guild_id", None) != gid:
|
|
1431
1436
|
continue
|
|
1437
|
+
# A view an earlier exit cascaded to is already gone.
|
|
1438
|
+
if view._torn_down():
|
|
1439
|
+
continue
|
|
1432
1440
|
try:
|
|
1433
1441
|
await view.exit()
|
|
1434
1442
|
exited += 1
|
|
@@ -1525,6 +1533,9 @@ class DevToolsCog(commands.Cog, name="cascadeui_devtools"):
|
|
|
1525
1533
|
# state half-torn-down.
|
|
1526
1534
|
failed = 0
|
|
1527
1535
|
for view_id, view in list(store.get_active_views().items()):
|
|
1536
|
+
# A view an earlier exit cascaded to is already gone.
|
|
1537
|
+
if view._torn_down():
|
|
1538
|
+
continue
|
|
1528
1539
|
try:
|
|
1529
1540
|
await view.exit()
|
|
1530
1541
|
except Exception as exc:
|
|
@@ -1535,6 +1546,13 @@ class DevToolsCog(commands.Cog, name="cascadeui_devtools"):
|
|
|
1535
1546
|
# added to ``StateStore.__init__`` flow through here automatically.
|
|
1536
1547
|
store.state = StateStore._build_initial_state()
|
|
1537
1548
|
|
|
1549
|
+
# The registry mirror is rebuilt rather than left empty: rows the exit
|
|
1550
|
+
# loop above did not delete are still on disk, and a mirror that has
|
|
1551
|
+
# forgotten them retires nothing when their key is exited later.
|
|
1552
|
+
manager = getattr(store, "persistence_manager", None)
|
|
1553
|
+
if manager is not None:
|
|
1554
|
+
manager.reseed_registry_mirror()
|
|
1555
|
+
|
|
1538
1556
|
# Observability: invalidate every @computed so cached aggregates
|
|
1539
1557
|
# recompute against the empty state on next access, and clear the
|
|
1540
1558
|
# selector memo so subscriber selectors do not short-circuit on
|
|
@@ -1669,7 +1687,8 @@ class DevToolsCog(commands.Cog, name="cascadeui_devtools"):
|
|
|
1669
1687
|
live panel. The cost is that a channel the bot will never see
|
|
1670
1688
|
again is re-fetched every startup. This surfaces how long each
|
|
1671
1689
|
one has been that way, and hands the delete decision to whoever
|
|
1672
|
-
runs the command
|
|
1690
|
+
runs the command, or to the daily sweep when the middleware sets
|
|
1691
|
+
``prune_unreachable_after_days``.
|
|
1673
1692
|
"""
|
|
1674
1693
|
store = get_store()
|
|
1675
1694
|
manager = getattr(store, "persistence_manager", None)
|