Skip to content

Prefixed commands

attrs class PrefixedCommandParameter

An object representing parameters in a prefixed command.

This class should not be instantiated directly.

Attr attributes:

Name Type Description
name str

The name of the parameter.

default Optional[Any]

The default value of the parameter.

type Type

The type of the parameter.

kind _ParameterKind

The kind of parameter this is as related to the function.

converters list

A list of the converter functions for the parameter that convert to its type.

greedy bool

Is the parameter greedy?

union bool

Is the parameter a union?

variable bool

Was the parameter marked as a variable argument?

consume_rest bool

Was the parameter marked to consume the rest of the input?

no_argument bool

Does this parameter have a converter that subclasses NoArgumentConverter?

Source code in naff/models/naff/prefixed_commands.py
@attrs.define(slots=True)
class PrefixedCommandParameter:
    """
    An object representing parameters in a prefixed command.

    This class should not be instantiated directly.
    """

    name: str = attrs.field(default=None, metadata=docs("The name of the parameter."))
    default: Optional[Any] = attrs.field(default=None, metadata=docs("The default value of the parameter."))
    type: Type = attrs.field(
        default=None, metadata=docs("The type of the parameter.")
    )  # yes i can use type here, mkdocs doesnt like that
    kind: inspect._ParameterKind = attrs.field(default=inspect.Parameter.POSITIONAL_OR_KEYWORD)
    """The kind of parameter this is as related to the function."""
    converters: list[Callable[["PrefixedContext", str], Any]] = attrs.field(
        factory=list, metadata=docs("A list of the converter functions for the parameter that convert to its type.")
    )
    greedy: bool = attrs.field(default=False, metadata=docs("Is the parameter greedy?"))
    union: bool = attrs.field(default=False, metadata=docs("Is the parameter a union?"))
    variable: bool = attrs.field(default=False, metadata=docs("Was the parameter marked as a variable argument?"))
    consume_rest: bool = attrs.field(
        default=False, metadata=docs("Was the parameter marked to consume the rest of the input?")
    )
    no_argument: bool = attrs.field(
        default=False, metadata=docs("Does this parameter have a converter that subclasses `NoArgumentConverter`?")
    )

    @property
    def optional(self) -> bool:
        """Is this parameter optional?"""
        return self.default != MISSING

property readonly optional: bool

Is this parameter optional?

attrs class PrefixedCommand (BaseCommand)

Attr attributes:

Name Type Description
extension Any

The extension this command belongs to

enabled bool

Whether this can be run at all

checks list

Any checks that must be checked before the command can run

cooldown Cooldown

An optional cooldown to apply to the command

max_concurrency MaxConcurrency

An optional maximum number of concurrent instances to apply to the command

callback Callable[..., Coroutine]

The coroutine to be called for this command

error_callback Callable[..., Coroutine]

The coroutine to be called when an error occurs

pre_run_callback Callable[..., Coroutine]

The coroutine to be called before the command is executed, but after the checks

post_run_callback Callable[..., Coroutine]

The coroutine to be called after the command has executed

name str

The name of the command.

parameters list

The paramters of the command.

aliases list

The list of aliases the command can be invoked under.

hidden bool

If True, help commands should not show this in the help output (unless toggled to do so).

ignore_extra bool

If True, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised. Defaults to True.

hierarchical_checking bool

If True and if the base of a subcommand, every subcommand underneath it will run this command's checks and cooldowns before its own. Otherwise, only the subcommand's checks are checked.

help Optional[str]

The long help text for the command.

brief Optional[str]

The short help text for the command.

parent Optional[PrefixedCommand]

The parent command, if applicable.

subcommands dict

A dict of all subcommands for the command.

Source code in naff/models/naff/prefixed_commands.py
@define()
class PrefixedCommand(BaseCommand):
    name: str = field(metadata=docs("The name of the command."))
    parameters: list[PrefixedCommandParameter] = field(metadata=docs("The paramters of the command."), factory=list)
    aliases: list[str] = field(
        metadata=docs("The list of aliases the command can be invoked under."),
        factory=list,
    )
    hidden: bool = field(
        metadata=docs("If `True`, help commands should not show this in the help output (unless toggled to do so)."),
        default=False,
    )
    ignore_extra: bool = field(
        metadata=docs(
            "If `True`, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c"
            " when only expecting a and b). Otherwise, an error is raised. Defaults to True."
        ),
        default=True,
    )
    hierarchical_checking: bool = field(
        metadata=docs(
            "If `True` and if the base of a subcommand, every subcommand underneath it will run this command's checks"
            " and cooldowns before its own. Otherwise, only the subcommand's checks are checked."
        ),
        default=True,
    )
    help: Optional[str] = field(metadata=docs("The long help text for the command."), default=None)
    brief: Optional[str] = field(metadata=docs("The short help text for the command."), default=None)
    parent: Optional["PrefixedCommand"] = field(metadata=docs("The parent command, if applicable."), default=None)
    subcommands: dict[str, "PrefixedCommand"] = field(
        metadata=docs("A dict of all subcommands for the command."), factory=dict
    )
    _usage: Optional[str] = field(default=None)
    _inspect_signature: Optional[inspect.Signature] = field(default=None)

    def __attrs_post_init__(self) -> None:
        super().__attrs_post_init__()  # we want checks to work

        # we have to do this afterwards as these rely on the callback
        # and its own value, which is impossible to get with attrs
        # methods, i think

        if self.help:
            self.help = inspect.cleandoc(self.help)
        else:
            self.help = inspect.getdoc(self.callback)
            if isinstance(self.help, bytes):
                self.help = self.help.decode("utf-8")

        if self.brief is None:
            self.brief = self.help.splitlines()[0] if self.help is not None else None

    @property
    def usage(self) -> str:
        """
        A string displaying how the command can be used.

        If no string is set, it will default to the command's signature.
        Useful for help commands.
        """
        return self._usage or self.signature

    @usage.setter
    def usage(self, usage: str) -> None:
        self._usage = usage

    @property
    def qualified_name(self) -> str:
        """Returns the full qualified name of this command."""
        name_deq = deque()
        command = self

        while command.parent is not None:
            name_deq.appendleft(command.name)
            command = command.parent

        name_deq.appendleft(command.name)
        return " ".join(name_deq)

    @property
    def all_subcommands(self) -> frozenset["PrefixedCommand"]:
        """Returns all unique subcommands underneath this command."""
        return frozenset(self.subcommands.values())

    @property
    def signature(self) -> str:
        """Returns a POSIX-like signature useful for help command output."""
        if not self.parameters:
            return ""

        results = []

        for param in self.parameters:
            anno = param.type
            name = param.name

            if typing.get_origin(anno) == Annotated:
                anno = typing.get_args(anno)[1]

            if not param.greedy and param.union:
                union_args = typing.get_args(anno)
                if len(union_args) == 2 and param.optional:
                    anno = union_args[0]

            if typing.get_origin(anno) is Literal:
                # it's better to list the values it can be than display the variable name itself
                name = "|".join(f'"{v}"' if isinstance(v, str) else str(v) for v in typing.get_args(anno))

            # we need to do a lot of manipulations with the signature
            # string, so using a deque as a string builder makes sense for performance
            result_builder: deque[str] = deque()

            if param.optional and param.default is not None:
                # it would be weird making it look like name=None
                result_builder.append(f"{name}={param.default}")
            else:
                result_builder.append(name)

            if param.variable:
                # this is inside the brackets
                result_builder.append("...")

            # surround the result with brackets
            if param.optional:
                result_builder.appendleft("[")
                result_builder.append("]")
            else:
                result_builder.appendleft("<")
                result_builder.append(">")

            if param.greedy:
                # this is outside the brackets, making it differentiable from
                # a variable argument
                result_builder.append("...")

            results.append("".join(result_builder))

        return " ".join(results)

    @property
    def is_subcommand(self) -> bool:
        """Return whether this command is a subcommand or not."""
        return bool(self.parent)

    def _parse_parameters(self) -> None:
        """
        Parses the parameters that this command has into a form naff can use.

        This is purposely seperated like this to allow "lazy parsing" - parsing
        as the command is added to a bot rather than being parsed immediately.
        This allows variables like "self" to be filtered out, and is useful for
        potential future additions.
        """
        # clear out old parameters just in case
        self.parameters = []

        if not self._inspect_signature:
            # we don't care about the ctx or self variables
            if self.has_binding:
                callback = functools.partial(self.callback, None, None)
            else:
                callback = functools.partial(self.callback, None)

            self._inspect_signature = inspect.signature(callback)

        params = self._inspect_signature.parameters

        # this is used by keyword-only and variable args to make sure there isn't more than one of either
        # mind you, we also don't want one keyword-only and one variable arg either
        finished_params = False

        for name, param in params.items():
            if finished_params:
                raise ValueError("Cannot have multiple keyword-only or variable arguments.")

            cmd_param = PrefixedCommandParameter()
            cmd_param.name = name
            cmd_param.kind = param.kind
            cmd_param.default = param.default if param.default is not param.empty else MISSING

            cmd_param.type = anno = param.annotation

            if typing.get_origin(anno) == Greedy:
                anno, default = _greedy_parse(anno, param)

                if default is not param.empty:
                    cmd_param.default = default
                cmd_param.greedy = True

            if typing.get_origin(anno) in {Union, UnionType}:
                cmd_param.union = True
                for arg in typing.get_args(anno):
                    if _check_for_no_arg(anno):
                        cmd_param.no_argument = True

                    if arg != NoneType:
                        converter = _get_converter(arg, name)
                        cmd_param.converters.append(converter)
                    elif not cmd_param.optional:  # d.py-like behavior
                        cmd_param.default = None
            else:
                if _check_for_no_arg(anno):
                    cmd_param.no_argument = True

                converter = _get_converter(anno, name)
                cmd_param.converters.append(converter)

            match param.kind:
                case param.KEYWORD_ONLY:
                    if cmd_param.greedy:
                        raise ValueError("Keyword-only arguments cannot be Greedy.")

                    cmd_param.consume_rest = True
                    finished_params = True
                case param.VAR_POSITIONAL:
                    if cmd_param.optional:
                        # there's a lot of parser ambiguities here, so i'd rather not
                        raise ValueError("Variable arguments cannot have default values or be Optional.")
                    if cmd_param.greedy:
                        raise ValueError("Variable arguments cannot be Greedy.")

                    cmd_param.variable = True
                    finished_params = True

            self.parameters.append(cmd_param)

        # we need to deal with subcommands too
        for command in self.all_subcommands:
            command._parse_parameters()

    def add_command(self, cmd: "PrefixedCommand") -> None:
        """
        Adds a command as a subcommand to this command.

        Args:
            cmd: The command to add
        """
        cmd.parent = self  # just so we know this is a subcommand

        if self.subcommands.get(cmd.name):
            raise ValueError(
                f"Duplicate command! Multiple commands share the name/alias: {self.qualified_name} {cmd.name}."
            )
        self.subcommands[cmd.name] = cmd

        for alias in cmd.aliases:
            if self.subcommands.get(alias):
                raise ValueError(
                    f"Duplicate command! Multiple commands share the name/alias: {self.qualified_name} {cmd.name}."
                )
            self.subcommands[alias] = cmd

    def remove_command(self, name: str) -> None:
        """
        Removes a command as a subcommand from this command.

        If an alias is specified, only the alias will be removed.

        Args:
            name: The command to remove.
        """
        command = self.subcommands.pop(name, None)

        if command is None:
            return

        if name in command.aliases:
            command.aliases.remove(name)
            return

        for alias in command.aliases:
            self.subcommands.pop(alias, None)

    def get_command(self, name: str) -> Optional["PrefixedCommand"]:
        """
        Gets a subcommand from this command. Can get subcommands of subcommands if needed.

        Args:
            name: The command to search for.

        Returns:
            PrefixedCommand: The command object, if found.
        """
        if " " not in name:
            return self.subcommands.get(name)

        names = name.split()
        if not names:
            return None

        cmd = self.subcommands.get(names[0])
        if not cmd or not cmd.subcommands:
            return cmd

        for name in names[1:]:
            try:
                cmd = cmd.subcommands[name]
            except (AttributeError, KeyError):
                return None

        return cmd

    def subcommand(
        self,
        name: Optional[str] = None,
        *,
        aliases: Optional[list[str]] = None,
        help: Optional[str] = None,
        brief: Optional[str] = None,
        usage: Optional[str] = None,
        enabled: bool = True,
        hidden: bool = False,
        ignore_extra: bool = True,
        hierarchical_checking: bool = True,
    ) -> (Callable[..., "PrefixedCommand"]):
        """
        A decorator to declare a subcommand for a prefixed command.

        Args:
            name: The name of the command. Defaults to the name of the coroutine.
            aliases: The list of aliases the command can be invoked under.
            help: The long help text for the command. Defaults to the docstring of the coroutine, if there is one.
            brief: The short help text for the command. Defaults to the first line of the help text, if there is one.
            usage: A string displaying how the command can be used. If no string is set, it will default to the command's signature. Useful for help commands.
            enabled: Whether this command can be run at all.
            hidden: If `True`, the default help command (when it is added) does not show this in the help output.
            ignore_extra: If `True`, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised.
            hierarchical_checking: If `True` and if the base of a subcommand, every subcommand underneath it will run this command's checks before its own. Otherwise, only the subcommand's checks are checked.
        """

        def wrapper(func: Callable) -> "PrefixedCommand":
            cmd = PrefixedCommand(  # type: ignore
                callback=func,
                name=name or func.__name__,
                aliases=aliases or [],
                help=help,
                brief=brief,
                usage=usage,  # type: ignore
                enabled=enabled,
                hidden=hidden,
                ignore_extra=ignore_extra,
                hierarchical_checking=hierarchical_checking,
            )
            self.add_command(cmd)
            return cmd

        return wrapper

    async def call_callback(self, callback: Callable, ctx: "PrefixedContext") -> None:
        """
        Runs the callback of this command.

        Args:
            callback (Callable: The callback to run. This is provided for compatibility with naff.
            ctx (naff.MessageContext): The context to use for this command.
        """
        # sourcery skip: remove-empty-nested-block, remove-redundant-if, remove-unnecessary-else
        if len(self.parameters) == 0:
            if ctx.args and not self.ignore_extra:
                raise BadArgument(f"Too many arguments passed to {self.name}.")
            return await self.call_with_binding(callback, ctx)
        else:
            # this is slightly costly, but probably worth it
            new_args: list[Any] = []
            kwargs: dict[str, Any] = {}
            args = _PrefixedArgsIterator(tuple(ctx.args))
            param_index = 0

            for arg in args:
                while param_index < len(self.parameters):
                    param = self.parameters[param_index]

                    if param.consume_rest:
                        arg = args.consume_rest()

                    if param.variable:
                        args_to_convert = args.get_rest_of_args()
                        new_arg = [await _convert(param, ctx, arg) for arg in args_to_convert]
                        new_arg = tuple(arg[0] for arg in new_arg)
                        new_args.extend(new_arg)
                        param_index += 1
                        break

                    if param.greedy:
                        greedy_args, broke_off = await _greedy_convert(param, ctx, args)

                        new_args.append(greedy_args)
                        param_index += 1
                        if broke_off:
                            args.back()

                        if param.default:
                            continue
                        else:
                            break

                    converted, used_default = await _convert(param, ctx, arg)
                    if param.kind in {
                        inspect.Parameter.POSITIONAL_ONLY,
                        inspect.Parameter.VAR_POSITIONAL,
                    }:
                        new_args.append(converted)
                    else:
                        kwargs[param.name] = converted
                    param_index += 1

                    if not used_default:
                        break

            if param_index < len(self.parameters):
                for param in self.parameters[param_index:]:
                    if param.no_argument:
                        converted, _ = await _convert(param, ctx, None)  # type: ignore
                        if not param.consume_rest:
                            new_args.append(converted)
                        else:
                            kwargs[param.name] = converted
                            break
                        continue

                    if not param.optional:
                        raise BadArgument(f"{param.name} is a required argument that is missing.")
                    else:
                        if param.kind in {
                            inspect.Parameter.POSITIONAL_ONLY,
                            inspect.Parameter.VAR_POSITIONAL,
                        }:
                            new_args.append(param.default)
                        else:
                            kwargs[param.name] = param.default
                            break
            elif not self.ignore_extra and not args.finished:
                raise BadArgument(f"Too many arguments passed to {self.name}.")

            return await self.call_with_binding(callback, ctx, *new_args, **kwargs)

async inherited method call_with_binding(self, callback, *args, **kwargs)

Call a given method using this objects _binding.

Parameters:

Name Type Description Default
callback Callable[..., Coroutine[Any, Any, Any]]

The callback to call.

required
Source code in naff/models/naff/prefixed_commands.py
async def call_with_binding(self, callback: Callable[..., Coroutine[Any, Any, Any]], *args, **kwargs) -> Any:
    """
    Call a given method using this objects _binding.

    Args:
        callback: The callback to call.
    """
    if self._binding:
        return await callback(self._binding, *args, **kwargs)
    return await callback(*args, **kwargs)

inherited method update_from_dict(self, data)

Updates object attribute(s) with new json data received from discord api.

Parameters:

Name Type Description Default
data Dict[str, Any]

The json data received from discord api.

required

Returns:

Type Description
~T

The updated object class instance.

Source code in naff/models/naff/prefixed_commands.py
def update_from_dict(self: Type[const.T], data: Dict[str, Any]) -> const.T:
    """
    Updates object attribute(s) with new json data received from discord api.

    Args:
        data: The json data received from discord api.

    Returns:
        The updated object class instance.

    """
    data = self._process_dict(data)
    for key, value in self._filter_kwargs(data, self._get_keys()).items():
        # todo improve
        setattr(self, key, value)

    return self

inherited method to_dict(self)

Exports object into dictionary representation, ready to be sent to discord api.

Returns:

Type Description
Dict[str, Any]

The exported dictionary.

Source code in naff/models/naff/prefixed_commands.py
def to_dict(self) -> Dict[str, Any]:
    """
    Exports object into dictionary representation, ready to be sent to discord api.

    Returns:
        The exported dictionary.

    """
    self._check_object()
    return serializer.to_dict(self)

inherited method error(self, call)

A decorator to declare a coroutine as one that will be run upon an error.

Source code in naff/models/naff/prefixed_commands.py
def error(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run upon an error."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("Error handler must be coroutine")
    self.error_callback = call
    return call

inherited method pre_run(self, call)

A decorator to declare a coroutine as one that will be run before the command.

Source code in naff/models/naff/prefixed_commands.py
def pre_run(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run before the command."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("pre_run must be coroutine")
    self.pre_run_callback = call
    return call

inherited method post_run(self, call)

A decorator to declare a coroutine as one that will be run after the command has.

Source code in naff/models/naff/prefixed_commands.py
def post_run(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run after the command has."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("post_run must be coroutine")
    self.post_run_callback = call
    return call

property writable usage: str

A string displaying how the command can be used.

If no string is set, it will default to the command's signature. Useful for help commands.

property readonly qualified_name: str

Returns the full qualified name of this command.

property readonly all_subcommands: frozenset

Returns all unique subcommands underneath this command.

property readonly signature: str

Returns a POSIX-like signature useful for help command output.

property readonly is_subcommand: bool

Return whether this command is a subcommand or not.

method add_command(self, cmd)

Adds a command as a subcommand to this command.

Parameters:

Name Type Description Default
cmd PrefixedCommand

The command to add

required
Source code in naff/models/naff/prefixed_commands.py
def add_command(self, cmd: "PrefixedCommand") -> None:
    """
    Adds a command as a subcommand to this command.

    Args:
        cmd: The command to add
    """
    cmd.parent = self  # just so we know this is a subcommand

    if self.subcommands.get(cmd.name):
        raise ValueError(
            f"Duplicate command! Multiple commands share the name/alias: {self.qualified_name} {cmd.name}."
        )
    self.subcommands[cmd.name] = cmd

    for alias in cmd.aliases:
        if self.subcommands.get(alias):
            raise ValueError(
                f"Duplicate command! Multiple commands share the name/alias: {self.qualified_name} {cmd.name}."
            )
        self.subcommands[alias] = cmd

method remove_command(self, name)

Removes a command as a subcommand from this command.

If an alias is specified, only the alias will be removed.

Parameters:

Name Type Description Default
name str

The command to remove.

required
Source code in naff/models/naff/prefixed_commands.py
def remove_command(self, name: str) -> None:
    """
    Removes a command as a subcommand from this command.

    If an alias is specified, only the alias will be removed.

    Args:
        name: The command to remove.
    """
    command = self.subcommands.pop(name, None)

    if command is None:
        return

    if name in command.aliases:
        command.aliases.remove(name)
        return

    for alias in command.aliases:
        self.subcommands.pop(alias, None)

method get_command(self, name)

Gets a subcommand from this command. Can get subcommands of subcommands if needed.

Parameters:

Name Type Description Default
name str

The command to search for.

required

Returns:

Type Description
PrefixedCommand

The command object, if found.

Source code in naff/models/naff/prefixed_commands.py
def get_command(self, name: str) -> Optional["PrefixedCommand"]:
    """
    Gets a subcommand from this command. Can get subcommands of subcommands if needed.

    Args:
        name: The command to search for.

    Returns:
        PrefixedCommand: The command object, if found.
    """
    if " " not in name:
        return self.subcommands.get(name)

    names = name.split()
    if not names:
        return None

    cmd = self.subcommands.get(names[0])
    if not cmd or not cmd.subcommands:
        return cmd

    for name in names[1:]:
        try:
            cmd = cmd.subcommands[name]
        except (AttributeError, KeyError):
            return None

    return cmd

method subcommand(self, name, *, aliases, help, brief, usage, enabled, hidden, ignore_extra, hierarchical_checking)

A decorator to declare a subcommand for a prefixed command.

Parameters:

Name Type Description Default
name Optional[str]

The name of the command. Defaults to the name of the coroutine.

None
aliases Optional[list[str]]

The list of aliases the command can be invoked under.

None
help Optional[str]

The long help text for the command. Defaults to the docstring of the coroutine, if there is one.

None
brief Optional[str]

The short help text for the command. Defaults to the first line of the help text, if there is one.

None
usage Optional[str]

A string displaying how the command can be used. If no string is set, it will default to the command's signature. Useful for help commands.

None
enabled bool

Whether this command can be run at all.

True
hidden bool

If True, the default help command (when it is added) does not show this in the help output.

False
ignore_extra bool

If True, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised.

True
hierarchical_checking bool

If True and if the base of a subcommand, every subcommand underneath it will run this command's checks before its own. Otherwise, only the subcommand's checks are checked.

True
Source code in naff/models/naff/prefixed_commands.py
def subcommand(
    self,
    name: Optional[str] = None,
    *,
    aliases: Optional[list[str]] = None,
    help: Optional[str] = None,
    brief: Optional[str] = None,
    usage: Optional[str] = None,
    enabled: bool = True,
    hidden: bool = False,
    ignore_extra: bool = True,
    hierarchical_checking: bool = True,
) -> (Callable[..., "PrefixedCommand"]):
    """
    A decorator to declare a subcommand for a prefixed command.

    Args:
        name: The name of the command. Defaults to the name of the coroutine.
        aliases: The list of aliases the command can be invoked under.
        help: The long help text for the command. Defaults to the docstring of the coroutine, if there is one.
        brief: The short help text for the command. Defaults to the first line of the help text, if there is one.
        usage: A string displaying how the command can be used. If no string is set, it will default to the command's signature. Useful for help commands.
        enabled: Whether this command can be run at all.
        hidden: If `True`, the default help command (when it is added) does not show this in the help output.
        ignore_extra: If `True`, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised.
        hierarchical_checking: If `True` and if the base of a subcommand, every subcommand underneath it will run this command's checks before its own. Otherwise, only the subcommand's checks are checked.
    """

    def wrapper(func: Callable) -> "PrefixedCommand":
        cmd = PrefixedCommand(  # type: ignore
            callback=func,
            name=name or func.__name__,
            aliases=aliases or [],
            help=help,
            brief=brief,
            usage=usage,  # type: ignore
            enabled=enabled,
            hidden=hidden,
            ignore_extra=ignore_extra,
            hierarchical_checking=hierarchical_checking,
        )
        self.add_command(cmd)
        return cmd

    return wrapper

async method call_callback(self, callback, ctx)

Runs the callback of this command.

Parameters:

Name Type Description Default
callback Callable

The callback to run. This is provided for compatibility with naff.

required
ctx naff.MessageContext

The context to use for this command.

required
Source code in naff/models/naff/prefixed_commands.py
async def call_callback(self, callback: Callable, ctx: "PrefixedContext") -> None:
    """
    Runs the callback of this command.

    Args:
        callback (Callable: The callback to run. This is provided for compatibility with naff.
        ctx (naff.MessageContext): The context to use for this command.
    """
    # sourcery skip: remove-empty-nested-block, remove-redundant-if, remove-unnecessary-else
    if len(self.parameters) == 0:
        if ctx.args and not self.ignore_extra:
            raise BadArgument(f"Too many arguments passed to {self.name}.")
        return await self.call_with_binding(callback, ctx)
    else:
        # this is slightly costly, but probably worth it
        new_args: list[Any] = []
        kwargs: dict[str, Any] = {}
        args = _PrefixedArgsIterator(tuple(ctx.args))
        param_index = 0

        for arg in args:
            while param_index < len(self.parameters):
                param = self.parameters[param_index]

                if param.consume_rest:
                    arg = args.consume_rest()

                if param.variable:
                    args_to_convert = args.get_rest_of_args()
                    new_arg = [await _convert(param, ctx, arg) for arg in args_to_convert]
                    new_arg = tuple(arg[0] for arg in new_arg)
                    new_args.extend(new_arg)
                    param_index += 1
                    break

                if param.greedy:
                    greedy_args, broke_off = await _greedy_convert(param, ctx, args)

                    new_args.append(greedy_args)
                    param_index += 1
                    if broke_off:
                        args.back()

                    if param.default:
                        continue
                    else:
                        break

                converted, used_default = await _convert(param, ctx, arg)
                if param.kind in {
                    inspect.Parameter.POSITIONAL_ONLY,
                    inspect.Parameter.VAR_POSITIONAL,
                }:
                    new_args.append(converted)
                else:
                    kwargs[param.name] = converted
                param_index += 1

                if not used_default:
                    break

        if param_index < len(self.parameters):
            for param in self.parameters[param_index:]:
                if param.no_argument:
                    converted, _ = await _convert(param, ctx, None)  # type: ignore
                    if not param.consume_rest:
                        new_args.append(converted)
                    else:
                        kwargs[param.name] = converted
                        break
                    continue

                if not param.optional:
                    raise BadArgument(f"{param.name} is a required argument that is missing.")
                else:
                    if param.kind in {
                        inspect.Parameter.POSITIONAL_ONLY,
                        inspect.Parameter.VAR_POSITIONAL,
                    }:
                        new_args.append(param.default)
                    else:
                        kwargs[param.name] = param.default
                        break
        elif not self.ignore_extra and not args.finished:
            raise BadArgument(f"Too many arguments passed to {self.name}.")

        return await self.call_with_binding(callback, ctx, *new_args, **kwargs)

function prefixed_command(name, *, aliases, help, brief, usage, enabled, hidden, ignore_extra, hierarchical_checking)

A decorator to declare a coroutine as a prefixed command.

Parameters:

Name Type Description Default
name Optional[str]

The name of the command. Defaults to the name of the coroutine.

None
aliases Optional[list[str]]

The list of aliases the command can be invoked under.

None
help Optional[str]

The long help text for the command. Defaults to the docstring of the coroutine, if there is one.

None
brief Optional[str]

The short help text for the command. Defaults to the first line of the help text, if there is one.

None
usage Optional[str]

A string displaying how the command can be used. If no string is set, it will default to the command's signature. Useful for help commands.

None
enabled bool

Whether this command can be run at all.

True
hidden bool

If True, the default help command (when it is added) does not show this in the help output.

False
ignore_extra bool

If True, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised.

True
hierarchical_checking bool

If True and if the base of a subcommand, every subcommand underneath it will run this command's checks before its own. Otherwise, only the subcommand's checks are checked.

True
Source code in naff/models/naff/prefixed_commands.py
def prefixed_command(
    name: Optional[str] = None,
    *,
    aliases: Optional[list[str]] = None,
    help: Optional[str] = None,
    brief: Optional[str] = None,
    usage: Optional[str] = None,
    enabled: bool = True,
    hidden: bool = False,
    ignore_extra: bool = True,
    hierarchical_checking: bool = True,
) -> Callable[..., PrefixedCommand]:
    """
    A decorator to declare a coroutine as a prefixed command.

    Args:
        name: The name of the command. Defaults to the name of the coroutine.
        aliases: The list of aliases the command can be invoked under.
        help: The long help text for the command. Defaults to the docstring of the coroutine, if there is one.
        brief: The short help text for the command. Defaults to the first line of the help text, if there is one.
        usage: A string displaying how the command can be used. If no string is set, it will default to the command's signature. Useful for help commands.
        enabled: Whether this command can be run at all.
        hidden: If `True`, the default help command (when it is added) does not show this in the help output.
        ignore_extra: If `True`, ignores extraneous strings passed to a command if all its requirements are met (e.g. ?foo a b c when only expecting a and b). Otherwise, an error is raised.
        hierarchical_checking: If `True` and if the base of a subcommand, every subcommand underneath it will run this command's checks before its own. Otherwise, only the subcommand's checks are checked.
    """

    def wrapper(func: Callable) -> PrefixedCommand:
        return PrefixedCommand(  # type: ignore
            callback=func,
            name=name or func.__name__,
            aliases=aliases or [],
            help=help,
            brief=brief,
            usage=usage,  # type: ignore
            enabled=enabled,
            hidden=hidden,
            ignore_extra=ignore_extra,
            hierarchical_checking=hierarchical_checking,
        )

    return wrapper