Skip to content

Command parameters broken for callbacks in closures #1602

Closed as not planned
Closed as not planned
@scottanderson42

Description

@scottanderson42

Summary

A slash command callback in a closure is treated as a class member

Reproduction Steps

  1. Create a closure with a command defined it it by either a decorator or add_application_command.
  2. Call the closure during bot startup.
  3. The command will fail because it assumes non-module-level functions are class methods.

Minimal Reproducible Code

import discord  # For great justice !!
client = discord.Bot()

def prepare_command(some_arg: str) -> discord.SlashCommand:
    async def rhubarb(
            ctx,
            command: discord.Option(str, "The thing to do, yo."),
            args: discord.Option(str, "The things to do it to, yo."),
    ):
        print("/rhubarb", command)

    slash_command = discord.SlashCommand(
        rhubarb,
        name="rhubarb",
        description="Do wanton acts with toes.",
        options=[
            discord.Option(str, name="command"),
            discord.Option(str, name="args"),
        ]
    )
    client.add_application_command(slash_command)

prepare_command("parsnips")

client.run("etcetcetc")

Expected Results

The command to be registered with the application.

Actually I also expected to win a complimentary vacation to Fargo, Minnesota as well, but I suspect there might be other issues with my code as well...

Actual Results

Traceback (most recent call last):
  File "/root/.cache/pypoetry/virtualenvs/rhubarb-9wL-Y1vf-py3.9/lib/python3.9/site-packages/discord/commands/core.py", line 737, in _match_option_param_names
    p_name, p_obj = next(params)
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/lib/python/rhubarb/startup.py", line 60, in <module>
    asyncio.run(bot.start_bot())
  File "/opt/lib/python/rhubarb/discord/bot.py", line 109, in start_bot
    rhubarb.prepare_command("parsnips")
  File "/opt/lib/python/rhubarb/discord/commands/rhubarb.py", line 27, in prepare_command
    slash_command = discord.SlashCommand(
  File "/root/.cache/pypoetry/virtualenvs/rhubarb-9wL-Y1vf-py3.9/lib/python3.9/site-packages/discord/commands/core.py", line 644, in __init__
    self.options: List[Option] = self._match_option_param_names(params, kwop)
  File "/root/.cache/pypoetry/virtualenvs/rhubarb-9wL-Y1vf-py3.9/lib/python3.9/site-packages/discord/commands/core.py", line 740, in _match_option_param_names
    raise ClientException("Too many arguments passed to the options kwarg.")
discord.errors.ClientException: Too many arguments passed to the options kwarg.

Intents

Defaults.

System Information

  • Python v3.9.7-final
  • py-cord v2.0.0-final
  • aiohttp v3.8.1
  • system info: Linux 5.10.104-linuxkit Update README.rst #1 SMP PREEMPT Thu Mar 17 17:05:54 UTC 2022

Checklist

  • I have searched the open issues for duplicates.
  • I have shown the entire traceback, if possible.
  • I have removed my token from display, if visible.

Additional Context

The problem lies in _check_required_params:

    def _check_required_params(self, params):
        params = iter(params.items())
        required_params = (
            ["self", "context"]
            if self.attached_to_group
            or self.cog
            or len(self.callback.__qualname__.split(".")) > 1  # <--- here be ye olde scallawag
            else ["context"]
        )

In a closure, the callback function's __qualname__ looks like this:

prepare_command.<locals>.rhubarb

And so the code assumes that it is a class instance method, meaning that it will be passed self as the first argument. The required params have one too many params, the first option parameter gets swallowed in _match_option_param_names as a result, and Bob's your uncle, stack trace ahoy!

Using a static class method for a callback probably has the same issue (because they receive neither self nor cls as their first parameter) but I have not tested that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingduplicateThis issue or pull request already exists

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions