Skip to content

Commit a2e486c

Browse files
merged min/max implementation (#55)
* merged min/max implementation * code cleanup * overloads for min/max
1 parent 4b15040 commit a2e486c

File tree

2 files changed

+80
-35
lines changed

2 files changed

+80
-35
lines changed

asyncstdlib/builtins.py

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -426,15 +426,39 @@ async def map(
426426
yield await result
427427

428428

429-
__MAX_DEFAULT = Sentinel("<no default>")
429+
__MIN_MAX_DEFAULT = Sentinel("<no default>")
430430

431431

432+
@overload
433+
async def max(iterable: AnyIterable[LT], *, key: None = ...) -> LT:
434+
...
435+
436+
437+
@overload
432438
async def max(
433-
iterable: AnyIterable[LT],
439+
iterable: AnyIterable[LT], *, key: None = ..., default: T
440+
) -> Union[LT, T]:
441+
...
442+
443+
444+
@overload
445+
async def max(iterable: AnyIterable[T1], *, key: Callable[[T1], LT] = ...) -> T1:
446+
...
447+
448+
449+
@overload
450+
async def max(
451+
iterable: AnyIterable[T1], *, key: Callable[[T1], LT] = ..., default: T2
452+
) -> Union[T1, T2]:
453+
...
454+
455+
456+
async def max(
457+
iterable: AnyIterable[Any],
434458
*,
435-
key: Optional[Callable[[LT], Any]] = None,
436-
default: LT = __MAX_DEFAULT, # type: ignore
437-
) -> LT:
459+
key: Optional[Callable[[Any], Any]] = None,
460+
default: Any = __MIN_MAX_DEFAULT,
461+
) -> Any:
438462
"""
439463
Return the largest item from an (async) iterable or from two or more values
440464
@@ -452,33 +476,39 @@ async def max(
452476
as it does not benefit from being ``async``.
453477
Use the builtin :py:func:`max` function instead.
454478
"""
455-
async with ScopedIter(iterable) as item_iter:
456-
best = await anext(item_iter, default=__MAX_DEFAULT)
457-
if best is __MAX_DEFAULT:
458-
if default is __MAX_DEFAULT:
459-
raise ValueError("max() arg is an empty sequence")
460-
return default
461-
if key is None:
462-
async for item in item_iter:
463-
if item > best:
464-
best = item
465-
else:
466-
key = _awaitify(key)
467-
best_key = await key(best)
468-
async for item in item_iter:
469-
item_key = await key(item)
470-
if item_key > best_key:
471-
best = item
472-
best_key = item_key
473-
return best
479+
return await _min_max(iterable, key, True, default)
480+
474481

482+
@overload
483+
async def min(iterable: AnyIterable[LT], *, key: None = ...) -> LT:
484+
...
475485

486+
487+
@overload
476488
async def min(
477-
iterable: AnyIterable[LT],
489+
iterable: AnyIterable[LT], *, key: None = ..., default: T
490+
) -> Union[LT, T]:
491+
...
492+
493+
494+
@overload
495+
async def min(iterable: AnyIterable[T1], *, key: Callable[[T1], LT] = ...) -> T1:
496+
...
497+
498+
499+
@overload
500+
async def min(
501+
iterable: AnyIterable[T1], *, key: Callable[[T1], LT] = ..., default: T2
502+
) -> Union[T1, T2]:
503+
...
504+
505+
506+
async def min(
507+
iterable: AnyIterable[Any],
478508
*,
479-
key: Optional[Callable[[LT], Any]] = None,
480-
default: LT = __MAX_DEFAULT, # type: ignore
481-
) -> LT:
509+
key: Optional[Callable[[Any], Any]] = None,
510+
default: Any = __MIN_MAX_DEFAULT,
511+
) -> Any:
482512
"""
483513
Return the smallest item from an (async) iterable or from two or more values
484514
@@ -496,22 +526,37 @@ async def min(
496526
as it does not benefit from being ``async``.
497527
Use the builtin :py:func:`min` function instead.
498528
"""
529+
return await _min_max(iterable, key, False, default)
530+
531+
532+
async def _min_max(
533+
iterable: AnyIterable[LT],
534+
key: Optional[Callable[[LT], Any]],
535+
invert: bool,
536+
default: LT = __MIN_MAX_DEFAULT, # type: ignore
537+
) -> LT:
538+
"""
539+
Implementation of ``min``/``max``
540+
541+
:param invert: compute ``max`` if ``True`` and ``min`` otherwise
542+
"""
499543
async with ScopedIter(iterable) as item_iter:
500-
best = await anext(item_iter, default=__MAX_DEFAULT)
501-
if best is __MAX_DEFAULT:
502-
if default is __MAX_DEFAULT:
503-
raise ValueError("min() arg is an empty sequence")
544+
best = await anext(item_iter, default=__MIN_MAX_DEFAULT)
545+
if best is __MIN_MAX_DEFAULT:
546+
if default is __MIN_MAX_DEFAULT:
547+
name = "max" if invert else "min"
548+
raise ValueError(f"{name}() arg is an empty sequence")
504549
return default
505550
if key is None:
506551
async for item in item_iter:
507-
if item < best:
552+
if invert ^ (item < best):
508553
best = item
509554
else:
510555
key = _awaitify(key)
511556
best_key = await key(best)
512557
async for item in item_iter:
513558
item_key = await key(item)
514-
if item_key < best_key:
559+
if invert ^ (item_key < best_key):
515560
best = item
516561
best_key = item_key
517562
return best

asyncstdlib/itertools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ async def zip_longest(
474474
if not iterables:
475475
return
476476
fill_iter = aiter(_repeat(fillvalue))
477-
async_iters = list(aiter(it) for it in iterables)
477+
async_iters = [aiter(it) for it in iterables]
478478
del iterables
479479
try:
480480
remaining = len(async_iters)

0 commit comments

Comments
 (0)