619 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			619 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
The MIT License (MIT)
 | 
						|
 | 
						|
Copyright (c) 2015-present Rapptz
 | 
						|
 | 
						|
Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
copy of this software and associated documentation files (the "Software"),
 | 
						|
to deal in the Software without restriction, including without limitation
 | 
						|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
Software is furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
The above copyright notice and this permission notice shall be included in
 | 
						|
all copies or substantial portions of the Software.
 | 
						|
 | 
						|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
						|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
DEALINGS IN THE SOFTWARE.
 | 
						|
"""
 | 
						|
 | 
						|
from __future__ import annotations
 | 
						|
 | 
						|
from .errors import (
 | 
						|
    BadFlagArgument,
 | 
						|
    CommandError,
 | 
						|
    MissingFlagArgument,
 | 
						|
    TooManyFlags,
 | 
						|
    MissingRequiredFlag,
 | 
						|
)
 | 
						|
 | 
						|
from discord.utils import resolve_annotation
 | 
						|
from .view import StringView
 | 
						|
from .converter import run_converters
 | 
						|
 | 
						|
from discord.utils import maybe_coroutine, MISSING
 | 
						|
from dataclasses import dataclass, field
 | 
						|
from typing import (
 | 
						|
    Dict,
 | 
						|
    Iterator,
 | 
						|
    Literal,
 | 
						|
    Optional,
 | 
						|
    Pattern,
 | 
						|
    Set,
 | 
						|
    TYPE_CHECKING,
 | 
						|
    Tuple,
 | 
						|
    List,
 | 
						|
    Any,
 | 
						|
    Type,
 | 
						|
    TypeVar,
 | 
						|
    Union,
 | 
						|
)
 | 
						|
 | 
						|
import inspect
 | 
						|
import sys
 | 
						|
import re
 | 
						|
 | 
						|
__all__ = (
 | 
						|
    'Flag',
 | 
						|
    'flag',
 | 
						|
    'FlagConverter',
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    from .context import Context
 | 
						|
 | 
						|
 | 
						|
@dataclass
 | 
						|
class Flag:
 | 
						|
    """Represents a flag parameter for :class:`FlagConverter`.
 | 
						|
 | 
						|
    The :func:`~discord.ext.commands.flag` function helps
 | 
						|
    create these flag objects, but it is not necessary to
 | 
						|
    do so. These cannot be constructed manually.
 | 
						|
 | 
						|
    Attributes
 | 
						|
    ------------
 | 
						|
    name: :class:`str`
 | 
						|
        The name of the flag.
 | 
						|
    aliases: List[:class:`str`]
 | 
						|
        The aliases of the flag name.
 | 
						|
    attribute: :class:`str`
 | 
						|
        The attribute in the class that corresponds to this flag.
 | 
						|
    default: Any
 | 
						|
        The default value of the flag, if available.
 | 
						|
    annotation: Any
 | 
						|
        The underlying evaluated annotation of the flag.
 | 
						|
    max_args: :class:`int`
 | 
						|
        The maximum number of arguments the flag can accept.
 | 
						|
        A negative value indicates an unlimited amount of arguments.
 | 
						|
    override: :class:`bool`
 | 
						|
        Whether multiple given values overrides the previous value.
 | 
						|
    """
 | 
						|
 | 
						|
    name: str = MISSING
 | 
						|
    aliases: List[str] = field(default_factory=list)
 | 
						|
    attribute: str = MISSING
 | 
						|
    annotation: Any = MISSING
 | 
						|
    default: Any = MISSING
 | 
						|
    max_args: int = MISSING
 | 
						|
    override: bool = MISSING
 | 
						|
    cast_to_dict: bool = False
 | 
						|
 | 
						|
    @property
 | 
						|
    def required(self) -> bool:
 | 
						|
        """:class:`bool`: Whether the flag is required.
 | 
						|
 | 
						|
        A required flag has no default value.
 | 
						|
        """
 | 
						|
        return self.default is MISSING
 | 
						|
 | 
						|
 | 
						|
def flag(
 | 
						|
    *,
 | 
						|
    name: str = MISSING,
 | 
						|
    aliases: List[str] = MISSING,
 | 
						|
    default: Any = MISSING,
 | 
						|
    max_args: int = MISSING,
 | 
						|
    override: bool = MISSING,
 | 
						|
) -> Any:
 | 
						|
    """Override default functionality and parameters of the underlying :class:`FlagConverter`
 | 
						|
    class attributes.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ------------
 | 
						|
    name: :class:`str`
 | 
						|
        The flag name. If not given, defaults to the attribute name.
 | 
						|
    aliases: List[:class:`str`]
 | 
						|
        Aliases to the flag name. If not given no aliases are set.
 | 
						|
    default: Any
 | 
						|
        The default parameter. This could be either a value or a callable that takes
 | 
						|
        :class:`Context` as its sole parameter. If not given then it defaults to
 | 
						|
        the default value given to the attribute.
 | 
						|
    max_args: :class:`int`
 | 
						|
        The maximum number of arguments the flag can accept.
 | 
						|
        A negative value indicates an unlimited amount of arguments.
 | 
						|
        The default value depends on the annotation given.
 | 
						|
    override: :class:`bool`
 | 
						|
        Whether multiple given values overrides the previous value. The default
 | 
						|
        value depends on the annotation given.
 | 
						|
    """
 | 
						|
    return Flag(name=name, aliases=aliases, default=default, max_args=max_args, override=override)
 | 
						|
 | 
						|
 | 
						|
def validate_flag_name(name: str, forbidden: Set[str]):
 | 
						|
    if not name:
 | 
						|
        raise ValueError('flag names should not be empty')
 | 
						|
 | 
						|
    for ch in name:
 | 
						|
        if ch.isspace():
 | 
						|
            raise ValueError(f'flag name {name!r} cannot have spaces')
 | 
						|
        if ch == '\\':
 | 
						|
            raise ValueError(f'flag name {name!r} cannot have backslashes')
 | 
						|
        if ch in forbidden:
 | 
						|
            raise ValueError(f'flag name {name!r} cannot have any of {forbidden!r} within them')
 | 
						|
 | 
						|
 | 
						|
def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any], locals: Dict[str, Any]) -> Dict[str, Flag]:
 | 
						|
    annotations = namespace.get('__annotations__', {})
 | 
						|
    case_insensitive = namespace['__commands_flag_case_insensitive__']
 | 
						|
    flags: Dict[str, Flag] = {}
 | 
						|
    cache: Dict[str, Any] = {}
 | 
						|
    names: Set[str] = set()
 | 
						|
    for name, annotation in annotations.items():
 | 
						|
        flag = namespace.pop(name, MISSING)
 | 
						|
        if isinstance(flag, Flag):
 | 
						|
            flag.annotation = annotation
 | 
						|
        else:
 | 
						|
            flag = Flag(name=name, annotation=annotation, default=flag)
 | 
						|
 | 
						|
        flag.attribute = name
 | 
						|
        if flag.name is MISSING:
 | 
						|
            flag.name = name
 | 
						|
 | 
						|
        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():
 | 
						|
            flag.default = annotation._construct_default
 | 
						|
 | 
						|
        if flag.aliases is MISSING:
 | 
						|
            flag.aliases = []
 | 
						|
 | 
						|
        # Add sensible defaults based off of the type annotation
 | 
						|
        # <type> -> (max_args=1)
 | 
						|
        # List[str] -> (max_args=-1)
 | 
						|
        # Tuple[int, ...] -> (max_args=1)
 | 
						|
        # Dict[K, V] -> (max_args=-1, override=True)
 | 
						|
        # Union[str, int] -> (max_args=1)
 | 
						|
        # Optional[str] -> (default=None, max_args=1)
 | 
						|
 | 
						|
        try:
 | 
						|
            origin = annotation.__origin__
 | 
						|
        except AttributeError:
 | 
						|
            # A regular type hint
 | 
						|
            if flag.max_args is MISSING:
 | 
						|
                flag.max_args = 1
 | 
						|
        else:
 | 
						|
            if origin is Union:
 | 
						|
                # typing.Union
 | 
						|
                if flag.max_args is MISSING:
 | 
						|
                    flag.max_args = 1
 | 
						|
                if annotation.__args__[-1] is type(None) and flag.default is MISSING:
 | 
						|
                    # typing.Optional
 | 
						|
                    flag.default = None
 | 
						|
            elif origin is tuple:
 | 
						|
                # typing.Tuple
 | 
						|
                # tuple parsing is e.g. `flag: peter 20`
 | 
						|
                # for Tuple[str, int] would give you flag: ('peter', 20)
 | 
						|
                if flag.max_args is MISSING:
 | 
						|
                    flag.max_args = 1
 | 
						|
            elif origin is list:
 | 
						|
                # typing.List
 | 
						|
                if flag.max_args is MISSING:
 | 
						|
                    flag.max_args = -1
 | 
						|
            elif origin is dict:
 | 
						|
                # typing.Dict[K, V]
 | 
						|
                # Equivalent to:
 | 
						|
                # typing.List[typing.Tuple[K, V]]
 | 
						|
                flag.cast_to_dict = True
 | 
						|
                if flag.max_args is MISSING:
 | 
						|
                    flag.max_args = -1
 | 
						|
                if flag.override is MISSING:
 | 
						|
                    flag.override = True
 | 
						|
            elif origin is Literal:
 | 
						|
                if flag.max_args is MISSING:
 | 
						|
                    flag.max_args = 1
 | 
						|
            else:
 | 
						|
                raise TypeError(f'Unsupported typing annotation {annotation!r} for {flag.name!r} flag')
 | 
						|
 | 
						|
        if flag.override is MISSING:
 | 
						|
            flag.override = False
 | 
						|
 | 
						|
        # Validate flag names are unique
 | 
						|
        name = flag.name.casefold() if case_insensitive else flag.name
 | 
						|
        if name in names:
 | 
						|
            raise TypeError(f'{flag.name!r} flag conflicts with previous flag or alias.')
 | 
						|
        else:
 | 
						|
            names.add(name)
 | 
						|
 | 
						|
        for alias in flag.aliases:
 | 
						|
            # Validate alias is unique
 | 
						|
            alias = alias.casefold() if case_insensitive else alias
 | 
						|
            if alias in names:
 | 
						|
                raise TypeError(f'{flag.name!r} flag alias {alias!r} conflicts with previous flag or alias.')
 | 
						|
            else:
 | 
						|
                names.add(alias)
 | 
						|
 | 
						|
        flags[flag.name] = flag
 | 
						|
 | 
						|
    return flags
 | 
						|
 | 
						|
 | 
						|
class FlagsMeta(type):
 | 
						|
    if TYPE_CHECKING:
 | 
						|
        __commands_is_flag__: bool
 | 
						|
        __commands_flags__: Dict[str, Flag]
 | 
						|
        __commands_flag_aliases__: Dict[str, str]
 | 
						|
        __commands_flag_regex__: Pattern[str]
 | 
						|
        __commands_flag_case_insensitive__: bool
 | 
						|
        __commands_flag_delimiter__: str
 | 
						|
        __commands_flag_prefix__: str
 | 
						|
 | 
						|
    def __new__(
 | 
						|
        cls: Type[type],
 | 
						|
        name: str,
 | 
						|
        bases: Tuple[type, ...],
 | 
						|
        attrs: Dict[str, Any],
 | 
						|
        *,
 | 
						|
        case_insensitive: bool = MISSING,
 | 
						|
        delimiter: str = MISSING,
 | 
						|
        prefix: str = MISSING,
 | 
						|
    ):
 | 
						|
        attrs['__commands_is_flag__'] = True
 | 
						|
 | 
						|
        try:
 | 
						|
            global_ns = sys.modules[attrs['__module__']].__dict__
 | 
						|
        except KeyError:
 | 
						|
            global_ns = {}
 | 
						|
 | 
						|
        frame = inspect.currentframe()
 | 
						|
        try:
 | 
						|
            if frame is None:
 | 
						|
                local_ns = {}
 | 
						|
            else:
 | 
						|
                if frame.f_back is None:
 | 
						|
                    local_ns = frame.f_locals
 | 
						|
                else:
 | 
						|
                    local_ns = frame.f_back.f_locals
 | 
						|
        finally:
 | 
						|
            del frame
 | 
						|
 | 
						|
        flags: Dict[str, Flag] = {}
 | 
						|
        aliases: Dict[str, str] = {}
 | 
						|
        for base in reversed(bases):
 | 
						|
            if base.__dict__.get('__commands_is_flag__', False):
 | 
						|
                flags.update(base.__dict__['__commands_flags__'])
 | 
						|
                aliases.update(base.__dict__['__commands_flag_aliases__'])
 | 
						|
                if case_insensitive is MISSING:
 | 
						|
                    attrs['__commands_flag_case_insensitive__'] = base.__dict__['__commands_flag_case_insensitive__']
 | 
						|
                if delimiter is MISSING:
 | 
						|
                    attrs['__commands_flag_delimiter__'] = base.__dict__['__commands_flag_delimiter__']
 | 
						|
                if prefix is MISSING:
 | 
						|
                    attrs['__commands_flag_prefix__'] = base.__dict__['__commands_flag_prefix__']
 | 
						|
 | 
						|
        if case_insensitive is not MISSING:
 | 
						|
            attrs['__commands_flag_case_insensitive__'] = case_insensitive
 | 
						|
        if delimiter is not MISSING:
 | 
						|
            attrs['__commands_flag_delimiter__'] = delimiter
 | 
						|
        if prefix is not MISSING:
 | 
						|
            attrs['__commands_flag_prefix__'] = prefix
 | 
						|
 | 
						|
        case_insensitive = attrs.setdefault('__commands_flag_case_insensitive__', False)
 | 
						|
        delimiter = attrs.setdefault('__commands_flag_delimiter__', ':')
 | 
						|
        prefix = attrs.setdefault('__commands_flag_prefix__', '')
 | 
						|
 | 
						|
        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})
 | 
						|
 | 
						|
        forbidden = set(delimiter).union(prefix)
 | 
						|
        for flag_name in flags:
 | 
						|
            validate_flag_name(flag_name, forbidden)
 | 
						|
        for alias_name in aliases:
 | 
						|
            validate_flag_name(alias_name, forbidden)
 | 
						|
 | 
						|
        regex_flags = 0
 | 
						|
        if case_insensitive:
 | 
						|
            flags = {key.casefold(): value for key, value in flags.items()}
 | 
						|
            aliases = {key.casefold(): value.casefold() for key, value in aliases.items()}
 | 
						|
            regex_flags = re.IGNORECASE
 | 
						|
 | 
						|
        keys = list(re.escape(k) for k in flags)
 | 
						|
        keys.extend(re.escape(a) for a in aliases)
 | 
						|
        keys = sorted(keys, key=lambda t: len(t), reverse=True)
 | 
						|
 | 
						|
        joined = '|'.join(keys)
 | 
						|
        pattern = re.compile(f'(({re.escape(prefix)})(?P<flag>{joined}){re.escape(delimiter)})', regex_flags)
 | 
						|
        attrs['__commands_flag_regex__'] = pattern
 | 
						|
        attrs['__commands_flags__'] = flags
 | 
						|
        attrs['__commands_flag_aliases__'] = aliases
 | 
						|
 | 
						|
        return type.__new__(cls, name, bases, attrs)
 | 
						|
 | 
						|
 | 
						|
async def tuple_convert_all(ctx: Context, argument: str, flag: Flag, converter: Any) -> Tuple[Any, ...]:
 | 
						|
    view = StringView(argument)
 | 
						|
    results = []
 | 
						|
    param: inspect.Parameter = ctx.current_parameter  # type: ignore
 | 
						|
    while not view.eof:
 | 
						|
        view.skip_ws()
 | 
						|
        if view.eof:
 | 
						|
            break
 | 
						|
 | 
						|
        word = view.get_quoted_word()
 | 
						|
        if word is None:
 | 
						|
            break
 | 
						|
 | 
						|
        try:
 | 
						|
            converted = await run_converters(ctx, converter, word, param)
 | 
						|
        except CommandError:
 | 
						|
            raise
 | 
						|
        except Exception as e:
 | 
						|
            raise BadFlagArgument(flag) from e
 | 
						|
        else:
 | 
						|
            results.append(converted)
 | 
						|
 | 
						|
    return tuple(results)
 | 
						|
 | 
						|
 | 
						|
async def tuple_convert_flag(ctx: Context, argument: str, flag: Flag, converters: Any) -> Tuple[Any, ...]:
 | 
						|
    view = StringView(argument)
 | 
						|
    results = []
 | 
						|
    param: inspect.Parameter = ctx.current_parameter  # type: ignore
 | 
						|
    for converter in converters:
 | 
						|
        view.skip_ws()
 | 
						|
        if view.eof:
 | 
						|
            break
 | 
						|
 | 
						|
        word = view.get_quoted_word()
 | 
						|
        if word is None:
 | 
						|
            break
 | 
						|
 | 
						|
        try:
 | 
						|
            converted = await run_converters(ctx, converter, word, param)
 | 
						|
        except CommandError:
 | 
						|
            raise
 | 
						|
        except Exception as e:
 | 
						|
            raise BadFlagArgument(flag) from e
 | 
						|
        else:
 | 
						|
            results.append(converted)
 | 
						|
 | 
						|
    if len(results) != len(converters):
 | 
						|
        raise BadFlagArgument(flag)
 | 
						|
 | 
						|
    return tuple(results)
 | 
						|
 | 
						|
 | 
						|
async def convert_flag(ctx, argument: str, flag: Flag, annotation: Any = None) -> Any:
 | 
						|
    param: inspect.Parameter = ctx.current_parameter  # type: ignore
 | 
						|
    annotation = annotation or flag.annotation
 | 
						|
    try:
 | 
						|
        origin = annotation.__origin__
 | 
						|
    except AttributeError:
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        if origin is tuple:
 | 
						|
            if annotation.__args__[-1] is Ellipsis:
 | 
						|
                return await tuple_convert_all(ctx, argument, flag, annotation.__args__[0])
 | 
						|
            else:
 | 
						|
                return await tuple_convert_flag(ctx, argument, flag, annotation.__args__)
 | 
						|
        elif origin is list:
 | 
						|
            # typing.List[x]
 | 
						|
            annotation = annotation.__args__[0]
 | 
						|
            return await convert_flag(ctx, argument, flag, annotation)
 | 
						|
        elif origin is Union and annotation.__args__[-1] is type(None):
 | 
						|
            # typing.Optional[x]
 | 
						|
            annotation = Union[annotation.__args__[:-1]]
 | 
						|
            return await run_converters(ctx, annotation, argument, param)
 | 
						|
        elif origin is dict:
 | 
						|
            # typing.Dict[K, V] -> typing.Tuple[K, V]
 | 
						|
            return await tuple_convert_flag(ctx, argument, flag, annotation.__args__)
 | 
						|
 | 
						|
    try:
 | 
						|
        return await run_converters(ctx, annotation, argument, param)
 | 
						|
    except CommandError:
 | 
						|
        raise
 | 
						|
    except Exception as e:
 | 
						|
        raise BadFlagArgument(flag) from e
 | 
						|
 | 
						|
 | 
						|
F = TypeVar('F', bound='FlagConverter')
 | 
						|
 | 
						|
 | 
						|
class FlagConverter(metaclass=FlagsMeta):
 | 
						|
    """A converter that allows for a user-friendly flag syntax.
 | 
						|
 | 
						|
    The flags are defined using :pep:`526` type annotations similar
 | 
						|
    to the :mod:`dataclasses` Python module. For more information on
 | 
						|
    how this converter works, check the appropriate
 | 
						|
    :ref:`documentation <ext_commands_flag_converter>`.
 | 
						|
 | 
						|
    .. container:: operations
 | 
						|
 | 
						|
        .. describe:: iter(x)
 | 
						|
 | 
						|
            Returns an iterator of ``(flag_name, flag_value)`` pairs. This allows it
 | 
						|
            to be, for example, constructed as a dict or a list of pairs.
 | 
						|
            Note that aliases are not shown.
 | 
						|
 | 
						|
    .. versionadded:: 2.0
 | 
						|
 | 
						|
    Parameters
 | 
						|
    -----------
 | 
						|
    case_insensitive: :class:`bool`
 | 
						|
        A class parameter to toggle case insensitivity of the flag parsing.
 | 
						|
        If ``True`` then flags are parsed in a case insensitive manner.
 | 
						|
        Defaults to ``False``.
 | 
						|
    prefix: :class:`str`
 | 
						|
        The prefix that all flags must be prefixed with. By default
 | 
						|
        there is no prefix.
 | 
						|
    delimiter: :class:`str`
 | 
						|
        The delimiter that separates a flag's argument from the flag's name.
 | 
						|
        By default this is ``:``.
 | 
						|
    """
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def get_flags(cls) -> Dict[str, Flag]:
 | 
						|
        """Dict[:class:`str`, :class:`Flag`]: A mapping of flag name to flag object this converter has."""
 | 
						|
        return cls.__commands_flags__.copy()
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def _can_be_constructible(cls) -> bool:
 | 
						|
        return all(not flag.required for flag in cls.__commands_flags__.values())
 | 
						|
 | 
						|
    def __iter__(self) -> Iterator[Tuple[str, Any]]:
 | 
						|
        for flag in self.__class__.__commands_flags__.values():
 | 
						|
            yield (flag.name, getattr(self, flag.attribute))
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    async def _construct_default(cls: Type[F], ctx: Context) -> F:
 | 
						|
        self: F = cls.__new__(cls)
 | 
						|
        flags = cls.__commands_flags__
 | 
						|
        for flag in flags.values():
 | 
						|
            if callable(flag.default):
 | 
						|
                default = await maybe_coroutine(flag.default, ctx)
 | 
						|
                setattr(self, flag.attribute, default)
 | 
						|
            else:
 | 
						|
                setattr(self, flag.attribute, flag.default)
 | 
						|
        return self
 | 
						|
 | 
						|
    def __repr__(self) -> str:
 | 
						|
        pairs = ' '.join([f'{flag.attribute}={getattr(self, flag.attribute)!r}' for flag in self.get_flags().values()])
 | 
						|
        return f'<{self.__class__.__name__} {pairs}>'
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    def parse_flags(cls, argument: str) -> Dict[str, List[str]]:
 | 
						|
        result: Dict[str, List[str]] = {}
 | 
						|
        flags = cls.__commands_flags__
 | 
						|
        aliases = cls.__commands_flag_aliases__
 | 
						|
        last_position = 0
 | 
						|
        last_flag: Optional[Flag] = None
 | 
						|
 | 
						|
        case_insensitive = cls.__commands_flag_case_insensitive__
 | 
						|
        for match in cls.__commands_flag_regex__.finditer(argument):
 | 
						|
            begin, end = match.span(0)
 | 
						|
            key = match.group('flag')
 | 
						|
            if case_insensitive:
 | 
						|
                key = key.casefold()
 | 
						|
 | 
						|
            if key in aliases:
 | 
						|
                key = aliases[key]
 | 
						|
 | 
						|
            flag = flags.get(key)
 | 
						|
            if last_position and last_flag is not None:
 | 
						|
                value = argument[last_position : begin - 1].lstrip()
 | 
						|
                if not value:
 | 
						|
                    raise MissingFlagArgument(last_flag)
 | 
						|
 | 
						|
                try:
 | 
						|
                    values = result[last_flag.name]
 | 
						|
                except KeyError:
 | 
						|
                    result[last_flag.name] = [value]
 | 
						|
                else:
 | 
						|
                    values.append(value)
 | 
						|
 | 
						|
            last_position = end
 | 
						|
            last_flag = flag
 | 
						|
 | 
						|
        # Add the remaining string to the last available flag
 | 
						|
        if last_position and last_flag is not None:
 | 
						|
            value = argument[last_position:].strip()
 | 
						|
            if not value:
 | 
						|
                raise MissingFlagArgument(last_flag)
 | 
						|
 | 
						|
            try:
 | 
						|
                values = result[last_flag.name]
 | 
						|
            except KeyError:
 | 
						|
                result[last_flag.name] = [value]
 | 
						|
            else:
 | 
						|
                values.append(value)
 | 
						|
 | 
						|
        # Verification of values will come at a later stage
 | 
						|
        return result
 | 
						|
 | 
						|
    @classmethod
 | 
						|
    async def convert(cls: Type[F], ctx: Context, argument: str) -> F:
 | 
						|
        """|coro|
 | 
						|
 | 
						|
        The method that actually converters an argument to the flag mapping.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        cls: Type[:class:`FlagConverter`]
 | 
						|
            The flag converter class.
 | 
						|
        ctx: :class:`Context`
 | 
						|
            The invocation context.
 | 
						|
        argument: :class:`str`
 | 
						|
            The argument to convert from.
 | 
						|
 | 
						|
        Raises
 | 
						|
        --------
 | 
						|
        FlagError
 | 
						|
            A flag related parsing error.
 | 
						|
        CommandError
 | 
						|
            A command related error.
 | 
						|
 | 
						|
        Returns
 | 
						|
        --------
 | 
						|
        :class:`FlagConverter`
 | 
						|
            The flag converter instance with all flags parsed.
 | 
						|
        """
 | 
						|
        arguments = cls.parse_flags(argument)
 | 
						|
        flags = cls.__commands_flags__
 | 
						|
 | 
						|
        self: F = cls.__new__(cls)
 | 
						|
        for name, flag in flags.items():
 | 
						|
            try:
 | 
						|
                values = arguments[name]
 | 
						|
            except KeyError:
 | 
						|
                if flag.required:
 | 
						|
                    raise MissingRequiredFlag(flag)
 | 
						|
                else:
 | 
						|
                    if callable(flag.default):
 | 
						|
                        default = await maybe_coroutine(flag.default, ctx)
 | 
						|
                        setattr(self, flag.attribute, default)
 | 
						|
                    else:
 | 
						|
                        setattr(self, flag.attribute, flag.default)
 | 
						|
                    continue
 | 
						|
 | 
						|
            if flag.max_args > 0 and len(values) > flag.max_args:
 | 
						|
                if flag.override:
 | 
						|
                    values = values[-flag.max_args :]
 | 
						|
                else:
 | 
						|
                    raise TooManyFlags(flag, values)
 | 
						|
 | 
						|
            # Special case:
 | 
						|
            if flag.max_args == 1:
 | 
						|
                value = await convert_flag(ctx, values[0], flag)
 | 
						|
                setattr(self, flag.attribute, value)
 | 
						|
                continue
 | 
						|
 | 
						|
            # Another special case, tuple parsing.
 | 
						|
            # Tuple parsing is basically converting arguments within the flag
 | 
						|
            # So, given flag: hello 20 as the input and Tuple[str, int] as the type hint
 | 
						|
            # We would receive ('hello', 20) as the resulting value
 | 
						|
            # This uses the same whitespace and quoting rules as regular parameters.
 | 
						|
            values = [await convert_flag(ctx, value, flag) for value in values]
 | 
						|
 | 
						|
            if flag.cast_to_dict:
 | 
						|
                values = dict(values)  # type: ignore
 | 
						|
 | 
						|
            setattr(self, flag.attribute, values)
 | 
						|
 | 
						|
        return self
 |