Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ext.commands): add LargeRange #857

Closed
wants to merge 13 commits into from
1 change: 1 addition & 0 deletions changelog/787.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add large numbers support to :class:`commands.Range` and add :class:`commands.LargeFloat`.
19 changes: 19 additions & 0 deletions disnake/ext/commands/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"PartialEmojiConversionFailure",
"BadBoolArgument",
"LargeIntConversionFailure",
"LargeFloatConversionFailure",
"MissingRole",
"BotMissingRole",
"MissingAnyRole",
Expand Down Expand Up @@ -551,6 +552,24 @@ def __init__(self, argument: str) -> None:
super().__init__(f"{argument} is not able to be converted to an integer")


class LargeFloatConversionFailure(BadArgument):
"""Exception raised when a large floating-point number argument was not able to be converted.

This inherits from :exc:`BadArgument`

.. versionadded:: 2.8

Attributes
----------
argument: :class:`str`
The argument that could not be converted to a floating-point number.
"""

def __init__(self, argument: str) -> None:
self.argument: str = argument
super().__init__(f"{argument} is not able to be converted to a floating-point number")


class DisabledCommand(CommandError):
"""Exception raised when the command being invoked is disabled.

Expand Down
53 changes: 46 additions & 7 deletions disnake/ext/commands/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"Range",
"String",
"LargeInt",
"LargeFloat",
"ParamInfo",
"Param",
"param",
Expand Down Expand Up @@ -414,6 +415,10 @@ class LargeInt(int):
"""Type for large integers in slash commands."""


class LargeFloat(float):
"""Type for large floating-point numbers in slash commands."""


# option types that require additional handling in verify_type
_VERIFY_TYPES: Final[FrozenSet[OptionType]] = frozenset((OptionType.user, OptionType.mentionable))

Expand Down Expand Up @@ -529,6 +534,7 @@ def __init__(
self.min_length = min_length
self.max_length = max_length
self.large = large
self._original_large_type = None

@property
def required(self) -> bool:
Expand Down Expand Up @@ -615,11 +621,35 @@ async def verify_type(self, inter: ApplicationCommandInteraction, argument: Any)

async def convert_argument(self, inter: ApplicationCommandInteraction, argument: Any) -> Any:
"""Convert a value if a converter is given"""
if self.large:
try:
argument = int(argument)
except ValueError:
raise errors.LargeIntConversionFailure(argument) from None
if self.large and self._original_large_type:
if self._original_large_type == int:
try:
argument = int(argument)
except ValueError:
raise errors.LargeIntConversionFailure(argument) from None
elif self._original_large_type == float:
try:
argument = float(argument)
print(argument)
except ValueError:
raise errors.LargeFloatConversionFailure(argument) from None

if (
self.min_value is not None
and self.max_value is not None
and (argument < self.min_value or argument > self.max_value)
):
raise errors.BadArgument(
f"Value for {self.name} must be in-between {self.min_value} and {self.max_value}!"
)
elif self.min_value is not None and argument < self.min_value:
raise errors.BadArgument(
f"Value for {self.name} must be greater than {self.min_value}!"
)
elif self.max_value is not None and argument > self.max_value:
raise errors.BadArgument(
f"Value for {self.name} must be less than {self.max_value}!"
)

if self.converter is None:
# TODO: Custom validators
Expand Down Expand Up @@ -680,18 +710,27 @@ def parse_annotation(self, annotation: Any, converter_mode: bool = False) -> boo
self.min_value = annotation.min_value
self.max_value = annotation.max_value
annotation = annotation.underlying_type

if self.min_value < -(2**53) or self.max_value > 2**53:
self.large = True
if isinstance(annotation, String):
self.min_length = annotation.min_length
self.max_length = annotation.max_length
annotation = annotation.underlying_type
if issubclass_(annotation, LargeInt):
self.large = True
annotation = int
if issubclass_(annotation, LargeFloat):
self.large = True
annotation = float

if self.large:
self.type = str
if annotation is not int:
raise TypeError("Large integers must be annotated with int or LargeInt")
if annotation not in (int, float):
raise TypeError(
"Large integers or large floats must be annotated with int, LargeInt, float, LargeFloat, or Range!"
)
self._original_large_type = annotation
elif annotation in self.TYPES:
self.type = annotation
elif (
Expand Down
10 changes: 10 additions & 0 deletions docs/ext/commands/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,13 @@ LargeInt

This is a class which inherits from :class:`int` to allow large numbers in slash commands, meant to be used only for annotations.

LargeFloat
~~~~~~~~~~

.. autoclass:: disnake.ext.commands.LargeFloat

This is a class which inherits from :class:`float` to allow large numbers in slash commands, meant to be used only for annotations.

Range
~~~~~

Expand Down Expand Up @@ -968,6 +975,9 @@ Exceptions
.. autoexception:: disnake.ext.commands.LargeIntConversionFailure
:members:

.. autoexception:: disnake.ext.commands.LargeFloatConversionFailure
:members:

.. autoexception:: disnake.ext.commands.MissingPermissions
:members:

Expand Down