compub/src/compub/utils.py

67 lines
2.5 KiB
Python

from operator import attrgetter
from typing import Any, Callable, TypeVar
T = TypeVar('T')
KeyFuncT = Callable[[T], Any]
_sentinel = object()
def multi_sort(obj_list: list[T], *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool]) -> None:
for param in reversed(parameters):
if isinstance(param, str):
obj_list.sort(key=attrgetter(param))
elif callable(param):
obj_list.sort(key=param)
else:
try:
param, reverse = param
assert isinstance(reverse, bool)
except (ValueError, TypeError):
raise ValueError(f"Sorting parameter {param} is neither a key nor a key-boolean-tuple.")
if isinstance(param, str):
obj_list.sort(key=attrgetter(param), reverse=reverse)
elif callable(param):
obj_list.sort(key=param, reverse=reverse)
else:
raise ValueError(f"Sorting key {param} is neither a string nor a callable.")
def multi_gt(left: T, right: T, *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool]) -> bool:
for param in parameters:
invert = False
if isinstance(param, str):
left_val, right_val = getattr(left, param), getattr(right, param)
elif callable(param):
left_val, right_val = param(left), param(right)
else:
try:
param, invert = param
assert isinstance(invert, bool)
except (ValueError, TypeError, AssertionError):
raise ValueError(f"Ordering parameter {param} is neither a key nor a key-boolean-tuple.")
if isinstance(param, str):
left_val, right_val = getattr(left, param), getattr(right, param)
elif callable(param):
left_val, right_val = param(left), param(right)
else:
raise ValueError(f"Ordering key {param} is neither a string nor a callable.")
if left_val == right_val:
continue
return left_val < right_val if invert else left_val > right_val
return False
def multi_max(obj_list: list[T], *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool],
default: Any = _sentinel) -> T:
try:
largest = obj_list[0]
except IndexError:
if default is not _sentinel:
return default
raise ValueError("Cannot get largest item from an empty list.")
for obj in obj_list[1:]:
if multi_gt(obj, largest, *parameters):
largest = obj
return largest