From 577a1b2f80bacc3277229b8b1b8f6cbace5d0095 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:48:00 +0530 Subject: [PATCH 1/3] Keep the first maximum when items tie or are unordered --- asyncstdlib/builtins.py | 6 ++++-- unittests/test_builtins.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/asyncstdlib/builtins.py b/asyncstdlib/builtins.py index 5c8f54c..0f42135 100644 --- a/asyncstdlib/builtins.py +++ b/asyncstdlib/builtins.py @@ -312,14 +312,16 @@ async def _min_max( raise ValueError(f"{name}() arg is an empty sequence") elif key is None: async for item in item_iter: - if invert ^ bool(item < best): + is_better = best < item if invert else item < best + if is_better: best = item else: key = _awaitify(key) best_key = await key(best) async for item in item_iter: item_key = await key(item) - if invert ^ (item_key < best_key): + is_better = best_key < item_key if invert else item_key < best_key + if is_better: best = item best_key = item_key return best diff --git a/unittests/test_builtins.py b/unittests/test_builtins.py index 382fddd..9dbe943 100644 --- a/unittests/test_builtins.py +++ b/unittests/test_builtins.py @@ -343,3 +343,36 @@ def collision_key(x: int) -> int: assert await a.sorted(values, key=awaitify(collision_key)) == sorted( values, key=collision_key ) + + +@pytest.mark.parametrize("key_kind", ["none", "sync", "async"]) +@pytest.mark.parametrize("async_input", [False, True]) +@sync +async def test_max_keeps_first_equal_item(key_kind, async_input): + class Record: + def __init__(self, priority, label): + self.priority = priority + self.label = label + + def __lt__(self, other): + return self.priority < other.priority + + records = [ + Record(1, "low"), + Record(3, "first"), + Record(3, "second"), + Record(2, "tail"), + ] + key = None if key_kind == "none" else lambda item: item.priority + expected = max(records, key=key) + actual = await a.max( + asyncify(records) if async_input else records, + key=awaitify(key) if key_kind == "async" else key, + ) + assert actual is expected + + +@pytest.mark.parametrize("values", [[float("nan"), 3.0], [3.0, float("nan"), 2.0]]) +@sync +async def test_max_does_not_replace_with_unordered_values(values): + assert await a.max(asyncify(values)) is max(values) From 6a3caf0189df94d25307bc36ff719a68881233ac Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:06:20 +0530 Subject: [PATCH 2/3] Simplify max comparisons and cover recovery after unordered values --- asyncstdlib/builtins.py | 6 ++---- unittests/test_builtins.py | 35 +++++++++++------------------------ 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/asyncstdlib/builtins.py b/asyncstdlib/builtins.py index 0f42135..0ea160c 100644 --- a/asyncstdlib/builtins.py +++ b/asyncstdlib/builtins.py @@ -312,16 +312,14 @@ async def _min_max( raise ValueError(f"{name}() arg is an empty sequence") elif key is None: async for item in item_iter: - is_better = best < item if invert else item < best - if is_better: + if best < item if invert else item < best: best = item else: key = _awaitify(key) best_key = await key(best) async for item in item_iter: item_key = await key(item) - is_better = best_key < item_key if invert else item_key < best_key - if is_better: + if best_key < item_key if invert else item_key < best_key: best = item best_key = item_key return best diff --git a/unittests/test_builtins.py b/unittests/test_builtins.py index 9dbe943..70a5c66 100644 --- a/unittests/test_builtins.py +++ b/unittests/test_builtins.py @@ -345,34 +345,21 @@ def collision_key(x: int) -> int: ) -@pytest.mark.parametrize("key_kind", ["none", "sync", "async"]) +@pytest.mark.parametrize("key", [None, lambda item: item.real]) +@pytest.mark.parametrize("async_key", [False, True]) @pytest.mark.parametrize("async_input", [False, True]) @sync -async def test_max_keeps_first_equal_item(key_kind, async_input): - class Record: - def __init__(self, priority, label): - self.priority = priority - self.label = label - - def __lt__(self, other): - return self.priority < other.priority - - records = [ - Record(1, "low"), - Record(3, "first"), - Record(3, "second"), - Record(2, "tail"), - ] - key = None if key_kind == "none" else lambda item: item.priority - expected = max(records, key=key) - actual = await a.max( - asyncify(records) if async_input else records, - key=awaitify(key) if key_kind == "async" else key, - ) - assert actual is expected +async def test_max_keeps_first_equal_item(key, async_key, async_input): + values = [float(value) for value in (1, 3, 3, 2)] + iterable = asyncify(values) if async_input else values + actual_key = awaitify(key) if async_key and key is not None else key + assert await a.max(iterable, key=actual_key) is max(values, key=key) -@pytest.mark.parametrize("values", [[float("nan"), 3.0], [3.0, float("nan"), 2.0]]) +@pytest.mark.parametrize( + "values", + [[float("nan"), 3.0], [3.0, float("nan"), 2.0], [2.0, float("nan"), 3.0]], +) @sync async def test_max_does_not_replace_with_unordered_values(values): assert await a.max(asyncify(values)) is max(values) From e73157156abf509e3d4fd9f5330a7d83c7bf1c47 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:50:47 +0530 Subject: [PATCH 3/3] Use portable records and direct callable test parameters --- unittests/test_builtins.py | 41 ++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/unittests/test_builtins.py b/unittests/test_builtins.py index 70a5c66..e11fb27 100644 --- a/unittests/test_builtins.py +++ b/unittests/test_builtins.py @@ -1,4 +1,5 @@ import random +from functools import total_ordering from typing import Any, Callable, Coroutine, TypeVar import pytest @@ -345,15 +346,39 @@ def collision_key(x: int) -> int: ) -@pytest.mark.parametrize("key", [None, lambda item: item.real]) -@pytest.mark.parametrize("async_key", [False, True]) -@pytest.mark.parametrize("async_input", [False, True]) +@total_ordering +class ComparableRecord: + def __init__(self, priority): + self.priority = priority + + def __eq__(self, other): + if not isinstance(other, ComparableRecord): + return NotImplemented + return self.priority == other.priority + + def __lt__(self, other): + if not isinstance(other, ComparableRecord): + return NotImplemented + return self.priority < other.priority + + +def record_priority(record): + return record.priority + + +@pytest.mark.parametrize( + "key, reference_key", + [ + (None, None), + (record_priority, record_priority), + (awaitify(record_priority), record_priority), + ], +) +@pytest.mark.parametrize("iterable", [iter, asyncify]) @sync -async def test_max_keeps_first_equal_item(key, async_key, async_input): - values = [float(value) for value in (1, 3, 3, 2)] - iterable = asyncify(values) if async_input else values - actual_key = awaitify(key) if async_key and key is not None else key - assert await a.max(iterable, key=actual_key) is max(values, key=key) +async def test_max_keeps_first_equal_item(key, reference_key, iterable): + values = [ComparableRecord(value) for value in (1, 3, 3, 2)] + assert await a.max(iterable(values), key=key) is max(values, key=reference_key) @pytest.mark.parametrize(