Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[types] Typing-checking error for commands.hybrid_command() and app_commands.command() decorators. #9788

Open
3 tasks done
Sachaa-Thanasius opened this issue Apr 14, 2024 · 0 comments
Labels
unconfirmed bug A bug report that needs triaging

Comments

@Sachaa-Thanasius
Copy link
Contributor

Sachaa-Thanasius commented Apr 14, 2024

Summary

Using the commands.hybrid_command() and app_commands.command() decorators on free functions results in type-checking errors when using attributes of that command (e.g. .error to decorate an error-handling function).

Reproduction Steps

I only noticed this after installing discord.py from the GitHub main branch, using a hybrid command on a free function, then using that command's .error decorator on another free function. Could be tied to advances in pyright.

Minimal Reproducible Code

from __future__ import annotations

import discord
from discord import app_commands
from discord.ext import commands
from typing_extensions import reveal_type


# ================================
# Regular command
# ================================

@commands.command()
async def text_roll(ctx: commands.Context[commands.Bot], expression: str | None = None) -> None: ...

@text_roll.error  # No error.
async def text_roll_error(ctx: commands.Context[commands.Bot], error: commands.CommandError) -> None: ...

reveal_type(text_roll)  # Type of "text_roll" is "Command[None, (expression: str | None = None), None]"


# ================================
# Hybrid command
# ================================

@commands.hybrid_command()
async def hybrid_roll(ctx: commands.Context[commands.Bot], expression: str | None = None) -> None: ...

@hybrid_roll.error  # Type of "error is partially unknown" # Long error message showing which parts are unknown, mainly CogT ...
async def hybrid_roll_error(ctx: commands.Context[commands.Bot], error: commands.CommandError) -> None: ...

# Type of "error" is partially unknown
#  Type of "error" is "(coro: ((Unknown, ContextT@error, CommandError) -> Coroutine[Any, Any, Any]) | ((ContextT@error, CommandError) -> Coroutine[Any, Any, Any]), /) -> (((Unknown, ContextT@error, CommandError) -> Coroutine[Any, Any, Any]) | ((ContextT@error, CommandError) -> Coroutine[Any, Any, Any]))"PylancereportUnknownMemberType

reveal_type(hybrid_roll)  # Type of "hybrid_roll" is "HybridCommand[Unknown, (expression: str | None = None), None]"


# ================================
# App command
# ================================

@app_commands.command()
async def app_roll(itx: discord.Interaction, expression: str | None = None) -> None: ...

@app_roll.error  # Type of "error is partially unknown" # Long error message showing which parts are unknown, mainly GroupT ...
async def app_roll_error(itx: discord.Interaction, error: app_commands.AppCommandError) -> None: ...

# Type of "error" is partially unknown
#   Type of "error" is "(coro: ((Unknown, Interaction[Any], AppCommandError) -> Coroutine[Any, Any, Any]) | ((Interaction[Any], AppCommandError) -> Coroutine[Any, Any, Any])) -> (((Unknown, Interaction[Any], AppCommandError) -> Coroutine[Any, Any, Any]) | ((Interaction[Any], AppCommandError) -> Coroutine[Any, Any, Any]))"PylancereportUnknownMemberType

reveal_type(app_roll)  # Type of "app_roll" is "Command[Unknown, (expression: str | None = None), None]"

Expected Results

No complaints from the type-checker on any of the decorated free functions.

Actual Results

Complaints from the type-checker about the hybrid and app commands, about only partially resolving the generic classes commands.hybrid.HybridCommand and app_commands.command.Command, which proliferates to many of those classes's methods.

Intents

N/A: This is related to typing. No bot is instantiated, therefore no Intents were used.

System Information

  • Python v3.11.6-final
  • discord.py v2.4.0-alpha
    • discord.py metadata: v2.4.0a4999+g425edd2e
  • aiohttp v3.9.4
  • system info: Windows 10 10.0.19044

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

This could potentially be fixed by using the same trick that commands.command uses (see below), but I'm not sure how much of a problem it would be to change the bound for CogT and GroupT in the files for command.hybrid_command and app_commands.command to include Optional[...], and that seems like it would be necessary:

if TYPE_CHECKING:
# Using a class to emulate a function allows for overloading the inner function in the decorator.
class _CommandDecorator:
@overload
def __call__(self, func: Callable[Concatenate[CogT, ContextT, P], Coro[T]], /) -> Command[CogT, P, T]:
...
@overload
def __call__(self, func: Callable[Concatenate[ContextT, P], Coro[T]], /) -> Command[None, P, T]:
...
def __call__(self, func: Callable[..., Coro[T]], /) -> Any:
...

@overload
def command(
name: str = ...,
**attrs: Any,
) -> _CommandDecorator:
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
unconfirmed bug A bug report that needs triaging
Projects
None yet
Development

No branches or pull requests

1 participant