Skip to content

Commit 326a68b

Browse files
committed
Fix #15234: make power_sort generic over Comparable items
- Add a Comparable protocol and TypeVar bound (PEP 695) so power_sort accepts any iterable of mutually comparable items instead of bare list - Rewrite run detection and merge comparisons to use only __lt__, which is all the protocol guarantees - Add doctest + shared test battery coverage, including the mixed-type TypeError failure mode
1 parent b3233c9 commit 326a68b

2 files changed

Lines changed: 19 additions & 8 deletions

File tree

‎sorts/power_sort.py‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@
2727

2828
from __future__ import annotations
2929

30-
from collections.abc import Callable
31-
from typing import Any
30+
from collections.abc import Callable, Iterable
31+
from typing import Any, Protocol
32+
33+
34+
class Comparable(Protocol):
35+
def __lt__(self, other: Any, /) -> bool: ...
3236

3337

3438
def _find_run(
@@ -79,7 +83,7 @@ def _find_run(
7983
arr[start:run_end] = reversed(arr[start:run_end])
8084
else:
8185
# Ascending run
82-
while run_end < end and key_func(arr[run_end]) >= key_func(arr[run_end - 1]):
86+
while run_end < end and not (key_func(arr[run_end]) < key_func(arr[run_end - 1])):
8387
run_end += 1
8488

8589
return run_end
@@ -176,7 +180,7 @@ def _merge(
176180

177181
# Merge the two runs
178182
while i < len(left) and j < len(right):
179-
if key_func(left[i]) <= key_func(right[j]):
183+
if not key_func(right[j]) < key_func(left[i]):
180184
arr[k] = left[i]
181185
i += 1
182186
else:
@@ -196,12 +200,12 @@ def _merge(
196200
k += 1
197201

198202

199-
def power_sort(
200-
collection: list,
203+
def power_sort[T: Comparable](
204+
collection: Iterable[T],
201205
*,
202-
key: Callable[[Any], Any] | None = None,
206+
key: Callable[[T], Any] | None = None,
203207
reverse: bool = False,
204-
) -> list:
208+
) -> list[T]:
205209
"""
206210
Sort a list using the PowerSort algorithm.
207211
@@ -247,6 +251,10 @@ def power_sort(
247251
[9, 8, 5, 2, 1]
248252
>>> power_sort(['apple', 'pie', 'a', 'longer'], key=len)
249253
['a', 'pie', 'apple', 'longer']
254+
>>> power_sort([1, "a"])
255+
Traceback (most recent call last):
256+
...
257+
TypeError: '<' not supported between instances of 'str' and 'int'
250258
>>> power_sort([(1, 'b'), (2, 'a'), (1, 'a')], key=lambda x: x[0])
251259
[(1, 'b'), (1, 'a'), (2, 'a')]
252260
>>> power_sort([1, 2, 3, 2, 1, 2, 3, 4])

‎tests/test_sorts.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from sorts.odd_even_transposition_single_threaded import odd_even_transposition
4343
from sorts.pancake_sort import pancake_sort
4444
from sorts.patience_sort import patience_sort
45+
from sorts.power_sort import power_sort
4546
from sorts.quick_sort import quick_sort
4647
from sorts.quick_sort_3_partition import three_way_radix_quicksort
4748
from sorts.recursive_insertion_sort import rec_insertion_sort
@@ -86,6 +87,7 @@ def test_heap_sort() -> None:
8687
odd_even_transposition,
8788
pancake_sort,
8889
patience_sort,
90+
power_sort,
8991
quick_sort,
9092
reverse_selection_sort,
9193
reversort,
@@ -165,6 +167,7 @@ def test_rec_insertion_sort(case) -> None:
165167
odd_even_transposition,
166168
pancake_sort,
167169
patience_sort,
170+
power_sort,
168171
reverse_selection_sort,
169172
reversort,
170173
selection_sort,

0 commit comments

Comments
 (0)