Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1431.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
|commands| Support :data:`typing.Annotated` for specifying converters in prefix commands in a type-safe way. See :ref:`Special Converters <ext_commands_converters_annotated>` for details.
6 changes: 6 additions & 0 deletions disnake/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,9 +1212,15 @@ def evaluate_annotation(
cache[tp] = evaluated
return evaluated

# Annotated[X, Y], where Y is the converter we need
if hasattr(tp, "__metadata__"):
tp = tp.__metadata__[0]
return evaluate_annotation(tp, globals, locals, cache)

# GenericAlias / UnionType
if hasattr(tp, "__args__"):
if not hasattr(tp, "__origin__"):
# n.b. this became obsolete in Python 3.14+, as `UnionType` and `Union` are the same thing now.
if tp.__class__ is UnionType:
converted = Union[tp.__args__]
return evaluate_annotation(converted, globals, locals, cache)
Expand Down
30 changes: 30 additions & 0 deletions docs/ext/commands/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ This can get tedious, so an inline advanced converter is possible through a :fun
else:
await ctx.send("Hm you're not so new.")

.. _ext_commands_discord_converters:

Discord Converters
++++++++++++++++++

Expand Down Expand Up @@ -552,6 +554,34 @@ The ``buy_sell`` parameter must be either the literal string ``"buy"`` or ``"sel

Note that ``typing.Literal[True]`` and ``typing.Literal[False]`` still follow the :class:`bool` converter rules.

.. _ext_commands_converters_annotated:

typing.Annotated
^^^^^^^^^^^^^^^^

.. versionadded:: |vnext|

With :data:`typing.Annotated`, you can use converters in a type-safe way.
Taking the example from :ref:`ext_commands_basic_converters` above, ``content`` is annotated
as ``to_upper`` (i.e. a converter function), while it would naturally be a :class:`str` at runtime;
this will likely trip up type-checkers such as pyright/mypy.

To avoid this, you can use :data:`typing.Annotated`, such that type-checkers consider the parameter
a :class:`str` while disnake will use the converter passed as the second argument to :data:`~typing.Annotated` at runtime:

.. code-block:: python3

from typing import Annotated

def to_upper(argument: str):
return argument.upper()

@bot.command()
async def up(ctx, *, content: Annotated[str, to_upper]):
await ctx.send(content)

This works with all types of converters mentioned on this page.

Greedy
^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion docs/ext/commands/slash_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Discord itself supports only a few built-in types which are guaranteed to be enf
- :class:`disnake.Attachment`
- :class:`disnake.abc.Snowflake`\*\*\*

All the other types may be converted implicitly, similarly to :ref:`ext_commands_basic_converters`
Other types may be converted implicitly, using the builtin :ref:`ext_commands_discord_converters`:

.. code-block:: python3

Expand Down
Loading