From a36af4a44a5cc1cf7679340a63a48d7cfea6df1b Mon Sep 17 00:00:00 2001 From: vmphase Date: Sun, 21 Jun 2026 19:43:29 +0300 Subject: [PATCH 1/2] refactor(utils): simplify get() --- discord/utils.py | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/discord/utils.py b/discord/utils.py index d09551f508..f70edf8465 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -69,19 +69,19 @@ if TYPE_CHECKING: from discord import ( + AppEmoji, + CategoryChannel, Client, - VoiceChannel, - TextChannel, ForumChannel, + Guild, + GuildEmoji, + Member, + Role, StageChannel, - CategoryChannel, + TextChannel, Thread, - Member, User, - Guild, - Role, - GuildEmoji, - AppEmoji, + VoiceChannel, ) from .errors import HTTPException, InvalidArgument, InvalidData @@ -568,25 +568,15 @@ def get(iterable: Iterable[T], **attrs: Any) -> T | None: Keyword arguments that denote attributes to search with. """ - # global -> local - _all = all - attrget = attrgetter - - # Special case the single element call - if len(attrs) == 1: - k, v = attrs.popitem() - pred = attrget(k.replace("__", ".")) - for elem in iterable: - if pred(elem) == v: - return elem - return None - converted = [ - (attrget(attr.replace("__", ".")), value) for attr, value in attrs.items() + (attrgetter(attr.replace("__", ".")), value) for attr, value in attrs.items() ] for elem in iterable: - if _all(pred(elem) == value for pred, value in converted): + for pred, value in converted: + if pred(elem) != value: + break + else: return elem return None From bc130b5df2b00cbd0ceb5ef84a79ab3bc7af294c Mon Sep 17 00:00:00 2001 From: vmphase Date: Sun, 21 Jun 2026 20:11:11 +0300 Subject: [PATCH 2/2] test(utils): add tests for .get() --- tests/test_utils.py | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/test_utils.py diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000000..5b5fdcf3d9 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,90 @@ +""" +The MIT License (MIT) + +Copyright (c) 2021-present Pycord Development + +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 types import SimpleNamespace + +import discord + + +def ns(**kwargs) -> SimpleNamespace: + return SimpleNamespace(**kwargs) + + +def test_get_single_attr_match(): + items = [ns(name="foo"), ns(name="bar"), ns(name="baz")] + result = discord.utils.get(items, name="bar") + assert result is not None + assert result.name == "bar" + + +def test_get_returns_first_match(): + items = [ns(name="foo", value=1), ns(name="foo", value=2)] + result = discord.utils.get(items, name="foo") + assert result is not None + assert result.value == 1 + + +def test_get_no_match_returns_none(): + items = [ns(name="foo"), ns(name="bar")] + assert discord.utils.get(items, name="baz") is None + + +def test_get_empty_iterable_returns_none(): + assert discord.utils.get([], name="foo") is None + + +def test_get_multiple_attrs(): + items = [ns(name="foo", value=1), ns(name="foo", value=2), ns(name="bar", value=1)] + result = discord.utils.get(items, name="foo", value=2) + assert result is not None + assert result.value == 2 + + +def test_get_multiple_attrs_no_match_returns_none(): + items = [ns(name="foo", value=1), ns(name="bar", value=2)] + assert discord.utils.get(items, name="foo", value=2) is None + + +def test_get_nested_attr(): + items = [ + ns(inner=ns(name="foo")), + ns(inner=ns(name="bar")), + ] + result = discord.utils.get(items, inner__name="bar") + assert result is not None + assert result.inner.name == "bar" + + +def test_get_match_at_start(): + items = [ns(name="foo"), ns(name="bar"), ns(name="baz")] + result = discord.utils.get(items, name="foo") + assert result is not None + assert result.name == "foo" + + +def test_get_match_at_end(): + items = [ns(name="foo"), ns(name="bar"), ns(name="baz")] + result = discord.utils.get(items, name="baz") + assert result is not None + assert result.name == "baz"