Make DEFAULT_CONTROLS and ReactionPredicate.*_EMOJIS immutable (#5666)

* Make ReactionPredicate.*_EMOJIS immutable

* Make menus.DEFAULT_CONTROLS immutable

* Actually convert NUMBER_EMOJIS to tuple
This commit is contained in:
jack1142 2022-04-09 21:35:11 +02:00 committed by GitHub
parent 6cb2378e2e
commit 96e8d8cdf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 23 deletions

View File

@ -5,23 +5,28 @@
import asyncio import asyncio
import contextlib import contextlib
import functools import functools
from typing import Iterable, List, Union from types import MappingProxyType
from typing import Callable, Iterable, List, Mapping, TypeVar, Union
import discord import discord
from .. import commands from .. import commands
from .predicates import ReactionPredicate from .predicates import ReactionPredicate
_T = TypeVar("_T")
_PageList = TypeVar("_PageList", List[str], List[discord.Embed])
_ReactableEmoji = Union[str, discord.Emoji] _ReactableEmoji = Union[str, discord.Emoji]
_ControlCallable = Callable[[commands.Context, _PageList, discord.Message, int, float, str], _T]
async def menu( async def menu(
ctx: commands.Context, ctx: commands.Context,
pages: Union[List[str], List[discord.Embed]], pages: _PageList,
controls: dict, controls: Mapping[str, _ControlCallable],
message: discord.Message = None, message: discord.Message = None,
page: int = 0, page: int = 0,
timeout: float = 30.0, timeout: float = 30.0,
): ) -> _T:
""" """
An emoji-based menu An emoji-based menu
@ -40,9 +45,10 @@ async def menu(
The command context The command context
pages: `list` of `str` or `discord.Embed` pages: `list` of `str` or `discord.Embed`
The pages of the menu. The pages of the menu.
controls: dict controls: Mapping[str, Callable],
A mapping of emoji to the function which handles the action for the A mapping of emoji to the function which handles the action for the
emoji. emoji. The signature of the function should be the same as of this function
and should additionally accept an ``emoji`` parameter of type `str`.
message: discord.Message message: discord.Message
The message representing the menu. Usually :code:`None` when first opening The message representing the menu. Usually :code:`None` when first opening
the menu the menu
@ -132,12 +138,12 @@ async def menu(
async def next_page( async def next_page(
ctx: commands.Context, ctx: commands.Context,
pages: list, pages: list,
controls: dict, controls: Mapping[str, _ControlCallable],
message: discord.Message, message: discord.Message,
page: int, page: int,
timeout: float, timeout: float,
emoji: str, emoji: str,
): ) -> _T:
if page == len(pages) - 1: if page == len(pages) - 1:
page = 0 # Loop around to the first item page = 0 # Loop around to the first item
else: else:
@ -148,12 +154,12 @@ async def next_page(
async def prev_page( async def prev_page(
ctx: commands.Context, ctx: commands.Context,
pages: list, pages: list,
controls: dict, controls: Mapping[str, _ControlCallable],
message: discord.Message, message: discord.Message,
page: int, page: int,
timeout: float, timeout: float,
emoji: str, emoji: str,
): ) -> _T:
if page == 0: if page == 0:
page = len(pages) - 1 # Loop around to the last item page = len(pages) - 1 # Loop around to the last item
else: else:
@ -164,12 +170,12 @@ async def prev_page(
async def close_menu( async def close_menu(
ctx: commands.Context, ctx: commands.Context,
pages: list, pages: list,
controls: dict, controls: Mapping[str, _ControlCallable],
message: discord.Message, message: discord.Message,
page: int, page: int,
timeout: float, timeout: float,
emoji: str, emoji: str,
): ) -> None:
with contextlib.suppress(discord.NotFound): with contextlib.suppress(discord.NotFound):
await message.delete() await message.delete()
@ -210,8 +216,10 @@ def start_adding_reactions(
return asyncio.create_task(task()) return asyncio.create_task(task())
DEFAULT_CONTROLS = { DEFAULT_CONTROLS: Mapping[str, _ControlCallable] = MappingProxyType(
{
"\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}": prev_page, "\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}": prev_page,
"\N{CROSS MARK}": close_menu, "\N{CROSS MARK}": close_menu,
"\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}": next_page, "\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}": next_page,
} }
)

View File

@ -881,19 +881,19 @@ class ReactionPredicate(Callable[[discord.Reaction, discord.abc.User], bool]):
) )
"""Tuple[str, str] : A tuple containing the tick emoji and cross emoji, in that order.""" """Tuple[str, str] : A tuple containing the tick emoji and cross emoji, in that order."""
ALPHABET_EMOJIS: ClassVar[List[str]] = [ ALPHABET_EMOJIS: ClassVar[Tuple[str, ...]] = tuple(
chr(code) chr(code)
for code in range( for code in range(
ord("\N{REGIONAL INDICATOR SYMBOL LETTER A}"), ord("\N{REGIONAL INDICATOR SYMBOL LETTER A}"),
ord("\N{REGIONAL INDICATOR SYMBOL LETTER Z}") + 1, ord("\N{REGIONAL INDICATOR SYMBOL LETTER Z}") + 1,
) )
] )
"""List[str] : A list of all 26 alphabetical letter emojis.""" """Tuple[str, ...] : A tuple of all 26 alphabetical letter emojis."""
NUMBER_EMOJIS: ClassVar[List[str]] = [ NUMBER_EMOJIS: ClassVar[Tuple[str, ...]] = tuple(
chr(code) + "\N{COMBINING ENCLOSING KEYCAP}" for code in range(ord("0"), ord("9") + 1) chr(code) + "\N{COMBINING ENCLOSING KEYCAP}" for code in range(ord("0"), ord("9") + 1)
] )
"""List[str] : A list of all single-digit number emojis, 0 through 9.""" """Tuple[str, ...] : A tuple of all single-digit number emojis, 0 through 9."""
def __init__( def __init__(
self, predicate: Callable[["ReactionPredicate", discord.Reaction, discord.abc.User], bool] self, predicate: Callable[["ReactionPredicate", discord.Reaction, discord.abc.User], bool]