mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-16 02:39:01 +00:00
[commands] Add support for positional flag parameters
This commit is contained in:
parent
9fab99acbc
commit
71358b8dce
@ -79,6 +79,10 @@ class Flag:
|
||||
description: :class:`str`
|
||||
The description of the flag. Shown for hybrid commands when they're
|
||||
used as application commands.
|
||||
positional: :class:`bool`
|
||||
Whether the flag is positional or not. There can only be one positional flag.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
|
||||
name: str = MISSING
|
||||
@ -89,6 +93,7 @@ class Flag:
|
||||
max_args: int = MISSING
|
||||
override: bool = MISSING
|
||||
description: str = MISSING
|
||||
positional: bool = MISSING
|
||||
cast_to_dict: bool = False
|
||||
|
||||
@property
|
||||
@ -109,6 +114,7 @@ def flag(
|
||||
override: bool = MISSING,
|
||||
converter: Any = MISSING,
|
||||
description: str = MISSING,
|
||||
positional: bool = MISSING,
|
||||
) -> Any:
|
||||
"""Override default functionality and parameters of the underlying :class:`FlagConverter`
|
||||
class attributes.
|
||||
@ -136,6 +142,10 @@ def flag(
|
||||
description: :class:`str`
|
||||
The description of the flag. Shown for hybrid commands when they're
|
||||
used as application commands.
|
||||
positional: :class:`bool`
|
||||
Whether the flag is positional or not. There can only be one positional flag.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
return Flag(
|
||||
name=name,
|
||||
@ -145,6 +155,7 @@ def flag(
|
||||
override=override,
|
||||
annotation=converter,
|
||||
description=description,
|
||||
positional=positional,
|
||||
)
|
||||
|
||||
|
||||
@ -171,6 +182,7 @@ def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any], locals: Dict[s
|
||||
flags: Dict[str, Flag] = {}
|
||||
cache: Dict[str, Any] = {}
|
||||
names: Set[str] = set()
|
||||
positional: Optional[Flag] = None
|
||||
for name, annotation in annotations.items():
|
||||
flag = namespace.pop(name, MISSING)
|
||||
if isinstance(flag, Flag):
|
||||
@ -183,6 +195,11 @@ def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any], locals: Dict[s
|
||||
if flag.name is MISSING:
|
||||
flag.name = name
|
||||
|
||||
if flag.positional:
|
||||
if positional is not None:
|
||||
raise TypeError(f"{flag.name!r} positional flag conflicts with {positional.name!r} flag.")
|
||||
positional = flag
|
||||
|
||||
annotation = flag.annotation = resolve_annotation(flag.annotation, globals, locals, cache)
|
||||
|
||||
if flag.default is MISSING and hasattr(annotation, '__commands_is_flag__') and annotation._can_be_constructible():
|
||||
@ -270,6 +287,7 @@ class FlagsMeta(type):
|
||||
__commands_flag_case_insensitive__: bool
|
||||
__commands_flag_delimiter__: str
|
||||
__commands_flag_prefix__: str
|
||||
__commands_flag_positional__: Optional[Flag]
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
@ -324,9 +342,13 @@ class FlagsMeta(type):
|
||||
delimiter = attrs.setdefault('__commands_flag_delimiter__', ':')
|
||||
prefix = attrs.setdefault('__commands_flag_prefix__', '')
|
||||
|
||||
positional: Optional[Flag] = None
|
||||
for flag_name, flag in get_flags(attrs, global_ns, local_ns).items():
|
||||
flags[flag_name] = flag
|
||||
aliases.update({alias_name: flag_name for alias_name in flag.aliases})
|
||||
if flag.positional:
|
||||
positional = flag
|
||||
attrs['__commands_flag_positional__'] = positional
|
||||
|
||||
forbidden = set(delimiter).union(prefix)
|
||||
for flag_name in flags:
|
||||
@ -500,10 +522,25 @@ class FlagConverter(metaclass=FlagsMeta):
|
||||
result: Dict[str, List[str]] = {}
|
||||
flags = cls.__commands_flags__
|
||||
aliases = cls.__commands_flag_aliases__
|
||||
positional_flag = cls.__commands_flag_positional__
|
||||
last_position = 0
|
||||
last_flag: Optional[Flag] = None
|
||||
|
||||
case_insensitive = cls.__commands_flag_case_insensitive__
|
||||
|
||||
if positional_flag is not None:
|
||||
match = cls.__commands_flag_regex__.search(argument)
|
||||
if match is not None:
|
||||
begin, end = match.span(0)
|
||||
value = argument[:begin].strip()
|
||||
else:
|
||||
value = argument.strip()
|
||||
last_position = len(argument)
|
||||
|
||||
if value:
|
||||
name = positional_flag.name.casefold() if case_insensitive else positional_flag.name
|
||||
result[name] = [value]
|
||||
|
||||
for match in cls.__commands_flag_regex__.finditer(argument):
|
||||
begin, end = match.span(0)
|
||||
key = match.group('flag')
|
||||
|
@ -778,6 +778,19 @@ This tells the parser that the ``members`` attribute is mapped to a flag named `
|
||||
the default value is an empty list. For greater customisability, the default can either be a value or a callable
|
||||
that takes the :class:`~ext.commands.Context` as a sole parameter. This callable can either be a function or a coroutine.
|
||||
|
||||
A positional flag can be defined by setting the :attr:`~ext.commands.Flag.positional` attribute to ``True``. This
|
||||
tells the parser that the content provided before the parsing occurs is part of the flag. This is useful for commands that
|
||||
require a parameter to be used first and the flags are optional, such as the following:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
class BanFlags(commands.FlagConverter):
|
||||
members: List[discord.Member] = commands.flag(name='member', positional=True, default=lambda ctx: [])
|
||||
reason: Optional[str] = None
|
||||
|
||||
.. note::
|
||||
Only one positional flag is allowed in a flag converter.
|
||||
|
||||
In order to customise the flag syntax we also have a few options that can be passed to the class parameter list:
|
||||
|
||||
.. code-block:: python3
|
||||
@ -797,11 +810,16 @@ In order to customise the flag syntax we also have a few options that can be pas
|
||||
nsfw: Optional[bool]
|
||||
slowmode: Optional[int]
|
||||
|
||||
# Hello there --bold True
|
||||
class Greeting(commands.FlagConverter):
|
||||
text: str = commands.flag(positional=True)
|
||||
bold: bool = False
|
||||
|
||||
.. note::
|
||||
|
||||
Despite the similarities in these examples to command like arguments, the syntax and parser is not
|
||||
a command line parser. The syntax is mainly inspired by Discord's search bar input and as a result
|
||||
all flags need a corresponding value.
|
||||
all flags need a corresponding value unless part of a positional flag.
|
||||
|
||||
Flag converters will only raise :exc:`~ext.commands.FlagError` derived exceptions. If an error is raised while
|
||||
converting a flag, :exc:`~ext.commands.BadFlagArgument` is raised instead and the original exception
|
||||
|
Loading…
x
Reference in New Issue
Block a user