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

1"""This module contains tools for collections.""" 

2 

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 

6 

7 

8_T = _TypeVar("_T") 

9 

10 

11# It does not seem like SupportsRichComparison is exposed anywhere from 

12# the python library. Therefore, _HasLT and _HasGT are implemented 

13# below. 

14 

15 

16class _HasLT(_Protocol): 

17 """Protocol for an object supporting __lt__.""" 

18 

19 def __lt__(self, other: _Any) -> bool: 

20 pass 

21 

22 

23class _HasGT(_Protocol): 

24 """Protocol for an object supporting __gt__.""" 

25 

26 def __gt__(self, other: _Any) -> bool: 

27 pass 

28 

29 

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""" 

34 

35 

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. 

46 

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. 

56 

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 """ 

62 

63 # Check if there is at least one element in the idx iterable. 

64 try: 

65 next(iter(idx)) 

66 except StopIteration: 

67 return 

68 

69 idx_b = sorted(idx, reverse=True) 

70 if idx_b[-1] < 0: 

71 raise IndexError("There were negative indices in 'idx'.") 

72 

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 

80 

81 removed = [src.pop(i) for i in checked_indices()] 

82 

83 if sorting_key is None: 

84 removed.reverse() 

85 else: 

86 removed.sort(key=sorting_key) 

87 

88 src[:] = src + removed