Coverage for src/pytest_samples/tools/_collectiontools.py: 100%
23 statements
« prev ^ index » next coverage.py v7.4.2, created at 2024-02-20 19:47 +0000
« prev ^ index » next coverage.py v7.4.2, created at 2024-02-20 19:47 +0000
1"""This module contains tools for collections."""
3from typing import Any as _Any, Callable as _Callable, \
4 Iterable as _Iterable, List as _List, Optional as _Optional, \
5 Protocol as _Protocol, TypeVar as _TypeVar, Union as _Union
8_T = _TypeVar("_T")
11# It does not seem like SupportsRichComparison is exposed anywhere from
12# the python library. Therefore, _HasLT and _HasGT are implemented
13# below.
16class _HasLT(_Protocol):
17 """Protocol for an object supporting __lt__."""
19 def __lt__(self, other: _Any) -> bool:
20 pass
23class _HasGT(_Protocol):
24 """Protocol for an object supporting __gt__."""
26 def __gt__(self, other: _Any) -> bool:
27 pass
30_KeyFunction = _Union[_Callable[[_T], _HasLT], _Callable[[_T], _HasGT]]
31"""Type of the functions that provide a sorting key for elements in a
32sequence.
33"""
36def move_idx_to_end(
37 src: _List[_T],
38 idx: _Iterable[int],
39 *,
40 sorting_key: _Optional[_KeyFunction[_T]] = None
41) -> None:
42 """Move a certain set of elements at given indices in a list to the
43 end of said list. By default, relative orderings will be preserved,
44 but a key function can be provided to determine the ordering of the
45 shifted elements.
47 Args:
48 src (List[T]): The list to modify in-place.
49 idx (Iterable[int]): The indices to shift to the end. Negative
50 indices will lead to an exception. The indices must be
51 unique.
52 sorting_key (KeyFunction[T], optional): A function that assings
53 a key to each element of `src` that should be shifted to
54 determine the ordering of the elements after the non-shifted
55 elements.
57 Raises:
58 IndexError: If negative indices appear in `idx`. If indices
59 appearing in `idx` are out-of-bounds with respect to `src`.
60 ValueError: If there are duplicate indices in `idx`.
61 """
63 # Check if there is at least one element in the idx iterable.
64 try:
65 next(iter(idx))
66 except StopIteration:
67 return
69 idx_b = sorted(idx, reverse=True)
70 if idx_b[-1] < 0:
71 raise IndexError("There were negative indices in 'idx'.")
73 def checked_indices():
74 prev_idx = None
75 for i in idx_b:
76 if i == prev_idx:
77 raise ValueError("There were duplicate indices in 'idx'.")
78 yield i
79 prev_idx = i
81 removed = [src.pop(i) for i in checked_indices()]
83 if sorting_key is None:
84 removed.reverse()
85 else:
86 removed.sort(key=sorting_key)
88 src[:] = src + removed