per_page<=0 raised ZeroDivisionError (==0) or yielded a negative max_pages (<0) at the
divmod; now a clear ValueError. on_timeout swallows a transient delete HTTPException (with
a log) so a best-effort cleanup doesn't surface as an unretrieved-task traceback. add the
module logger.
Signed-off-by: disqualifier <dev@disqualifier.me>
M-3: the cache-length guard used 'cache is not None', so cache=[] ([] is not None) hit the
length check and raised at construction — contradicting README/CLAUDE that say a falsy
cache disables the button (matching the render path 'if self.cache:'). guard now uses
'if cache' so [] and None both disable; a non-empty too-short cache still raises.
dpy_paginator-F2: update_buttons() now ANDs nav with max_pages>=2, so a single-page
re-render (update_page -> _build_render_kwargs, default nav=True) no longer resurrects the
nav row over the page's custom buttons. start()'s single-page branch simplified accordingly.
verified by execution with real discord.py: []-disables and None-disables both construct,
real cache renders, too-short still raises; single-page re-render keeps nav suppressed +
custom button, multi-page control still shows nav.
Signed-off-by: disqualifier <dev@disqualifier.me>
- a dict page with a wrong-typed embed/embeds/file/files key now raises a clear
paginator-side ValueError instead of forwarding it to discord.py as a conflicting
kwarg (opaque TypeError) (L12)
- interaction_check uses 'author_id is None' so an author_id of 0 still restricts (nit).
Signed-off-by: disqualifier <dev@disqualifier.me>
a single-page result (max_pages < 2) suppressed the navigation row by dropping the
whole view, which also discarded the consumer's custom per-page buttons. now: if the
page carries custom buttons, keep the view and rebuild with update_buttons(nav=False)
— nav items suppressed, custom buttons kept, and stop() NOT called so their callbacks
still fire. a page with no custom buttons keeps the original drop-the-view behavior.
verified by execution against real discord.py: single page + custom button -> start()
-> callback FIRES on click (view kept, stop() not called); negative control on the old
code drops the button entirely; the no-button single-page case is unregressed.
Signed-off-by: disqualifier <dev@disqualifier.me>
start() put ephemeral into send_kwargs unconditionally, so the raw Messageable.send() path raised TypeError (discord.py's channel send has no ephemeral param). ephemeral is now passed only on the interaction-response paths (followup.send / response.send_message), which support it; raw-channel send never receives it.
Signed-off-by: disqualifier <dev@disqualifier.me>
delete_message_after was stored but never read and no on_timeout override existed, so the documented 'delete the message on timeout' never happened. added on_timeout that deletes self.message when the flag is set, swallowing only the expected discord.NotFound (already deleted) / discord.Forbidden (no permission) and letting any unexpected error surface.
Signed-off-by: disqualifier <dev@disqualifier.me>
get_page() out-of-range/negative wrap reset to page 0 but returned pages[0] (a single item) even with per_page>1, mis-shaping the page downstream; it now reroutes through get_page(0) so the normal slice logic applies. cache shorter than max_pages now raises ValueError at construction instead of an IndexError when the cache button is clicked on a later page.
Signed-off-by: disqualifier <dev@disqualifier.me>
empty pages gave max_pages=0; get_page(0)/start() then IndexError'd on self.pages[0].
guard in __init__ with a clear ValueError instead of a deferred crash at render.
verified: ButtonPaginator([]) -> ValueError; non-empty and single-page intact.
Signed-off-by: disqualifier <dev@disqualifier.me>