Skip to content
Open
Show file tree
Hide file tree
Changes from all 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/1488.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :class:`~disnake.ext.commands.ColourConverter` accepting ``classmethod`` names that do not return :class:`Colour`, for example ``holographic_style``.
3 changes: 3 additions & 0 deletions disnake/colour.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ def dark_embed(cls) -> Self:
"""
return cls(0x2B2D31)

# NOTE: when adding new factory methods, also update _COLOUR_FACTORY_NAMES
# in disnake/ext/commands/converter.py

@classmethod
def holographic_style(cls) -> tuple[Self, Self, Self]:
"""A factory method that returns a tuple of :class:`Colour` with values of
Expand Down
49 changes: 46 additions & 3 deletions disnake/ext/commands/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,50 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.Thread:
return GuildChannelConverter._resolve_thread(ctx, argument, "threads", disnake.Thread)


# set of Colour.xyz() method names supported by ColourConverter
_COLOUR_FACTORY_NAMES: frozenset[str] = frozenset(
{
"default",
"random",
"teal",
"dark_teal",
"brand_green",
"green",
"dark_green",
"blue",
"dark_blue",
"purple",
"dark_purple",
"magenta",
"dark_magenta",
"gold",
"dark_gold",
"orange",
"dark_orange",
"brand_red",
"red",
"dark_red",
"lighter_grey",
"lighter_gray",
"dark_grey",
"dark_gray",
"light_grey",
"light_gray",
"darker_grey",
"darker_gray",
"og_blurple",
"old_blurple",
"blurple",
"greyple",
"dark_theme",
"fuchsia",
"yellow",
"light_embed",
"dark_embed",
}
)


class ColourConverter(Converter[disnake.Colour]):
"""Converts to a :class:`~disnake.Colour`.

Expand Down Expand Up @@ -748,10 +792,9 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.Color:
return self.parse_rgb(arg)

arg = arg.replace(" ", "_")
method = getattr(disnake.Colour, arg, None)
if arg.startswith("from_") or method is None or not inspect.ismethod(method):
if arg not in _COLOUR_FACTORY_NAMES:
raise BadColourArgument(arg)
return method()
return getattr(disnake.Colour, arg)()


ColorConverter = ColourConverter
Expand Down
70 changes: 70 additions & 0 deletions tests/ext/commands/test_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: MIT

from unittest import mock

import pytest

import disnake
from disnake.ext.commands import BadColourArgument
from disnake.ext.commands.converter import ColourConverter


@pytest.fixture
def converter() -> ColourConverter:
return ColourConverter()


@pytest.fixture
def ctx() -> mock.Mock:
return mock.Mock()


class TestColourConverter:
@pytest.mark.asyncio
@pytest.mark.parametrize(
("arg", "expected_value"),
[
("red", 0xE74C3C),
("dark_green", 0x1F8B4C),
("blurple", 0x5865F2),
("dark theme", 0x313338),
("og blurple", 0x7289DA),
],
)
async def test_valid_colour_names(
self, converter: ColourConverter, ctx: mock.Mock, arg: str, expected_value: int
) -> None:
result = await converter.convert(ctx, arg)
assert isinstance(result, disnake.Colour)
assert result.value == expected_value

@pytest.mark.asyncio
@pytest.mark.parametrize(
"arg",
[
"holographic_style",
"holographic style",
"from_rgb",
"from_hsv",
"from_hex",
"to_rgb",
"value",
"__init__",
"nonexistent",
],
)
async def test_invalid_colour_names(
self, converter: ColourConverter, ctx: mock.Mock, arg: str
) -> None:
with pytest.raises(BadColourArgument):
await converter.convert(ctx, arg)

@pytest.mark.asyncio
async def test_hex_format(self, converter: ColourConverter, ctx: mock.Mock) -> None:
result = await converter.convert(ctx, "#ff0000")
assert result.value == 0xFF0000

@pytest.mark.asyncio
async def test_rgb_format(self, converter: ColourConverter, ctx: mock.Mock) -> None:
result = await converter.convert(ctx, "rgb(255, 0, 0)")
assert result.value == 0xFF0000