mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-12 16:59:50 +00:00
Add support for range validation in options
This commit is contained in:
parent
3cf3065c02
commit
e5e5b61ad0
@ -42,6 +42,7 @@ from ..message import Attachment
|
||||
__all__ = (
|
||||
'Transformer',
|
||||
'Transform',
|
||||
'Range',
|
||||
)
|
||||
|
||||
T = TypeVar('T')
|
||||
@ -220,30 +221,27 @@ class _TransformMetadata:
|
||||
self.metadata: Type[Transformer] = metadata
|
||||
|
||||
|
||||
def _dynamic_transformer(
|
||||
def _make_range_transformer(
|
||||
opt_type: AppCommandOptionType,
|
||||
*,
|
||||
channel_types: List[ChannelType] = MISSING,
|
||||
min: Optional[Union[int, float]] = None,
|
||||
max: Optional[Union[int, float]] = None,
|
||||
) -> Type[Transformer]:
|
||||
types = channel_types or []
|
||||
|
||||
async def transform(cls, interaction: Interaction, value: Any) -> Any:
|
||||
return value
|
||||
|
||||
ns = {
|
||||
'type': classmethod(lambda _: opt_type),
|
||||
'channel_types': classmethod(lambda _: types),
|
||||
'min_value': classmethod(lambda _: min),
|
||||
'max_value': classmethod(lambda _: max),
|
||||
'transform': classmethod(transform),
|
||||
}
|
||||
return type('_DynamicTransformer', (Transformer,), ns)
|
||||
return type('RangeTransformer', (Transformer,), ns)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Annotated as Transform
|
||||
from typing_extensions import Annotated as Range
|
||||
else:
|
||||
|
||||
class Transform:
|
||||
@ -275,6 +273,60 @@ else:
|
||||
|
||||
return _TransformMetadata(transformer)
|
||||
|
||||
class Range:
|
||||
"""A type annotation that can be applied to a parameter to require a numeric type
|
||||
to fit within the range provided.
|
||||
|
||||
During type checking time this is equivalent to :obj:`py:Annotated` so type checkers understand
|
||||
the intent of the code.
|
||||
|
||||
Some example ranges:
|
||||
|
||||
- ``Range[int, 10]`` means the minimum is 10 with no maximum.
|
||||
- ``Range[int, None, 10]`` means the maximum is 10 with no minimum.
|
||||
- ``Range[int, 1, 10]`` means the minimum is 1 and the maximum is 10.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
@app_commands.command()
|
||||
async def range(interaction: discord.Interaction, value: app_commands.Range[10:12]):
|
||||
await interaction.response.send_message(f'Your value is {value}', ephemeral=True)
|
||||
"""
|
||||
|
||||
def __class_getitem__(cls, obj) -> _TransformMetadata:
|
||||
if not isinstance(obj, tuple):
|
||||
raise TypeError(f'expected tuple for arguments, received {obj.__class__!r} instead')
|
||||
|
||||
if len(obj) == 2:
|
||||
obj = (*obj, None)
|
||||
elif len(obj) != 3:
|
||||
raise TypeError('Range accepts either two or three arguments with the first being the type of range.')
|
||||
|
||||
obj_type, min, max = obj
|
||||
|
||||
if min is None and max is None:
|
||||
raise TypeError('Range must not be empty')
|
||||
|
||||
if min is not None and max is not None:
|
||||
# At this point max and min are both not none
|
||||
if type(min) != type(max):
|
||||
raise TypeError('Both min and max in Range must be the same type')
|
||||
|
||||
if obj_type is int:
|
||||
opt_type = AppCommandOptionType.integer
|
||||
elif obj_type is float:
|
||||
opt_type = AppCommandOptionType.number
|
||||
else:
|
||||
raise TypeError(f'expected int or float as range type, received {obj_type!r} instead')
|
||||
|
||||
transformer = _make_range_transformer(opt_type, min=obj_type(min), max=obj_type(max))
|
||||
return _TransformMetadata(transformer)
|
||||
|
||||
|
||||
def passthrough_transformer(opt_type: AppCommandOptionType) -> Type[Transformer]:
|
||||
class _Generated(Transformer):
|
||||
|
Loading…
x
Reference in New Issue
Block a user