diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebad593ab..d5f97cc42 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,8 +21,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools wheel "coverage[toml]" pytest pytest-asyncio pytest-cov pytest-mock - pip install -U -r requirements.txt + python -m pip install -e .[test] - name: Run tests shell: bash diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index 80082d0f4..c4d0a3702 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -160,12 +160,6 @@ def get_signature_parameters( if annotation is Greedy: raise TypeError('Unparameterized Greedy[...] is disallowed in signature.') - if hasattr(annotation, '__metadata__'): - # Annotated[X, Y] can access Y via __metadata__ - metadata = annotation.__metadata__ - if len(metadata) >= 1: - annotation = metadata[0] - params[name] = parameter.replace(annotation=annotation) return params diff --git a/discord/ext/commands/flags.py b/discord/ext/commands/flags.py index 138a607c1..69d7468f7 100644 --- a/discord/ext/commands/flags.py +++ b/discord/ext/commands/flags.py @@ -164,11 +164,6 @@ def get_flags(namespace: Dict[str, Any], globals: Dict[str, Any], locals: Dict[s flag.name = name annotation = flag.annotation = resolve_annotation(flag.annotation, globals, locals, cache) - if hasattr(annotation, '__metadata__'): - # Annotated[X, Y] can access Y via __metadata__ - metadata = annotation.__metadata__ - if len(metadata) >= 1: - annotation = flag.annotation = metadata[0] if flag.default is MISSING and hasattr(annotation, '__commands_is_flag__') and annotation._can_be_constructible(): flag.default = annotation._construct_default diff --git a/discord/utils.py b/discord/utils.py index ab5b4a60a..9d554c09e 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -1179,6 +1179,11 @@ def evaluate_annotation( cache[tp] = evaluated return evaluated + if hasattr(tp, '__metadata__'): + # Annotated[X, Y] can access Y via __metadata__ + metadata = tp.__metadata__[0] + return evaluate_annotation(metadata, globals, locals, cache) + if hasattr(tp, '__args__'): implicit_str = True is_literal = False diff --git a/setup.py b/setup.py index 1b42bf787..65cc0e011 100644 --- a/setup.py +++ b/setup.py @@ -40,14 +40,20 @@ extras_require = { 'sphinx==4.4.0', 'sphinxcontrib_trio==1.1.2', 'sphinxcontrib-websupport', - 'typing-extensions', - 'requests', + 'typing-extensions>=4.3,<5', ], 'speed': [ 'aiohttp[speedups]', 'orjson>=3.5.4', ], - 'test': ['coverage[toml]', 'pytest', 'pytest-asyncio', 'pytest-cov', 'pytest-mock'], + 'test': [ + 'coverage[toml]', + 'pytest', + 'pytest-asyncio', + 'pytest-cov', + 'pytest-mock', + 'typing-extensions>=4.3,<5', + ] } setup( diff --git a/tests/test_annotated_annotation.py b/tests/test_annotated_annotation.py new file mode 100644 index 000000000..51664b31d --- /dev/null +++ b/tests/test_annotated_annotation.py @@ -0,0 +1,52 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from __future__ import annotations +from typing import Optional +from typing_extensions import Annotated + +from discord.ext import commands + +import pytest + +def test_annotated_annotation(): + def to_hex(arg: str) -> int: + return int(arg, 16) + + class Flag(commands.FlagConverter): + thing: Annotated[int, to_hex] + + assert Flag.get_flags()['thing'].annotation == to_hex + + @commands.command() + async def bar(ctx: commands.Context, param: Annotated[float, Optional[int]]): + pass + + assert bar.clean_params['param'].annotation == Optional[int] + + @commands.command() + async def nested(ctx: commands.Context, param: Optional[Annotated[str, int]]): + pass + + assert nested.clean_params['param'].annotation == Optional[int]