Rapptz / discord.py

An API wrapper for Discord written in Python.
http://discordpy.rtfd.org/en/latest
MIT License
14.72k stars 3.74k forks source link

Invalid or missing invoked_subcommand in commands.Command #9016

Open Aguileitus opened 1 year ago

Aguileitus commented 1 year ago

Summary

As per the docs, commands.Command has a invoked_subcommand attribute, so either the property is missing in the Command class or the attribute should not be listed in the docs

Reproduction Steps

Create a command and try to call invoked_subcommand attribute

Minimal Reproducible Code

command.invoked_subcommand

Expected Results

Calling command.invoked_subcommand returns the invoked subcommand.

Actual Results

Traceback (most recent call last): File "D:...\venv\lib\site-packages\discord\ext\commands\core.py", line 190, in wrapped ret = await coro(*args, **kwargs) File "D:...\venv\lib\site-packages\discord\ext\commands\help.py", line 977, in command_callback return await self.send_command_help(cmd) File "D:...\cogs\help.py", line 232, in send_command_help embed.add_field(name="Uso", value=self.get_command_usage(command)) File "D:...\cogs\help.py", line 104, in get_command_usage print(command.invoked_subcommand) AttributeError: 'HybridCommand' object has no attribute 'invoked_subcommand'

Intents

members, message_content

System Information

Checklist

Additional Context

https://discordpy.readthedocs.io/en/latest/ext/commands/api.html?highlight=hybrid_command#discord.ext.commands.Command.invoked_subcommand

https://github.com/Rapptz/discord.py/blob/master/discord/ext/commands/core.py#L339

kastyne commented 3 weeks ago

I was able to reproduce this with the following snippet, on v2.4.0.

import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(command_prefix='?', intents=intents)

@bot.group()
async def foo(ctx: commands.Context) -> None:
    print(f"> {ctx.message.content}\n"
          f"{ctx.invoked_subcommand if hasattr(ctx, 'invoked_subcommand') else 'NoAttr'}," 
          f"{ctx.command.invoked_subcommand if hasattr(ctx.command, 'invoked_subcommand') else 'NoAttr'}")

@foo.command()
async def bar(ctx: commands.Context) -> None:
    return

bot.run('token')

Running the two commands gives the following output:

> ?foo
None,NoAttr
> ?foo bar
foo bar,NoAttr

This seems a bit confusing as one might expect that since command.invoked_subcommand is typed as Optional, the attribute will at least exist, but that doesn't look to be the case here.

Aside: I skimmed the core.py file; from my limited understanding, it appears that invoked_subcommand is a property of the Context associated with the Command/Group class instead of the class itself (which would explain the results of the snippet). Please correct me if I misunderstood!

pythonmcpi commented 3 weeks ago

invoked_subcommand should be a property on Context. It doesn't make sense for it to exist on Command or Group (and it doesn't even though the docstring says it does).

invoked_subcommand was added to the docstring for Command in commit af4e3ad five years ago, but it didn't exist as a property on Command back then either.

If you're trying to retrieve the subcommand invoked from a group command, use Context.invoked_subcommand instead.