Skip to content

fix: implement PEP 563 annotations handling in SlashCommand#3233

Closed
code-wolf-byte wants to merge 2 commits into
Pycord-Development:masterfrom
code-wolf-byte:fix/pep563-annotations-slash-options
Closed

fix: implement PEP 563 annotations handling in SlashCommand#3233
code-wolf-byte wants to merge 2 commits into
Pycord-Development:masterfrom
code-wolf-byte:fix/pep563-annotations-slash-options

Conversation

@code-wolf-byte
Copy link
Copy Markdown

@code-wolf-byte code-wolf-byte commented May 21, 2026

Caution

Feature freeze is active, as described by our Release Schedule.

This is a bug fix, not a feature addition. Per the
Release Schedule,
fixes remain eligible during feature freeze.

Summary

Fixes slash command option parsing when from __future__ import annotations (PEP 563)
is active in a user's codebase. Under PEP 563, all annotations are stored as strings at
definition time rather than being evaluated eagerly. _parse_options was reading
p_obj.annotation directly, so when a user wrote:

from __future__ import annotations
import discord
from discord import Option

@bot.slash_command()
async def greet(ctx, user: Option(discord.Member, "Pick a member")):
    ...

…pycord received the string "Option(discord.Member, 'Pick a member')" instead of the
real Option instance. The command registered silently, but crashed with:

TypeError: issubclass() arg 1 must be a class

the first time a user invoked it (_invoke called issubclass(op._raw_type, Enum) where
_raw_type was a plain string).

This was first reported in #513 (2021). PR #1251 closed that issue by promoting the
param: type = Option(...) default-value pattern as the canonical API — a workaround,
not a fix to the underlying annotation parsing.

Changes in this PR:

  • _parse_options and _match_option_param_names in discord/commands/core.py now
    call typing.get_type_hints(self.callback, include_extras=True) instead of reading
    p_obj.annotation directly. get_type_hints evaluates string annotations back to
    their original types regardless of whether PEP 563 is active. A bare except Exception
    falls back to the raw annotation if hint resolution fails (e.g. unresolvable forward
    references), so the change is backwards-compatible.
  • A defensive isinstance(op._raw_type, type) guard is added before the
    issubclass(op._raw_type, Enum) call in _invoke, preventing a TypeError crash
    even if a string _raw_type ever slips through from another code path.

AI disclosure: Generative AI (Claude, Anthropic) was used to identify and analyze this bug, produce partial code changes, and write the tests. The author has reviewed and understands all changes made.

Information

  • This PR fixes an issue.
  • This PR adds something new (e.g. new method or parameters).
  • This PR is a breaking change (e.g. methods or parameters removed/renamed).
  • This PR is not a code change (e.g. documentation, README, typehinting,
    examples, ...).

Checklist

  • I have searched the open pull requests for duplicates.
  • If code changes were made then they have been tested.
    • I have updated the documentation to reflect the changes.
  • If type: ignore comments were used, a comment is also left explaining why.
  • I have updated the changelog to include these changes.
  • AI Usage has been disclosed.
    • If AI has been used, I understand fully what the code does

@pycord-app
Copy link
Copy Markdown

pycord-app Bot commented May 21, 2026

Thanks for opening this pull request!
Please make sure you have read the Contributing Guidelines and Code of Conduct.

This pull request can be checked-out with:

git fetch origin pull/3233/head:pr-3233
git checkout pr-3233

This pull request can be installed with:

pip install git+https://github.com/Pycord-Development/pycord@refs/pull/3233/head

@plun1331
Copy link
Copy Markdown
Member

your tests are failing

@Paillat-dev
Copy link
Copy Markdown
Member

We do not accept slop PRs. You clearly show no understanding of the broader context, and CI is failing.

Furthermore, this is a known issue, which has already been discussed internally, as well as #2919 and #2919. We have no plans on accepting external fixed, feature additions or other changes on the Options related code at this time in order to focus on a better refactor of it.

@github-project-automation github-project-automation Bot moved this from Todo to Done in Pycord May 21, 2026
@Paillat-dev Paillat-dev added the invalid This doesn't seem right label May 21, 2026
@Pycord-Development Pycord-Development locked as spam and limited conversation to collaborators May 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

invalid This doesn't seem right

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants