67 lines
1.9 KiB
Python
67 lines
1.9 KiB
Python
from datetime import date
|
|
from typing import Any, Generator, TypeVar
|
|
|
|
|
|
def safe_division(numerator: int | float, denominator: int | float) -> int | float:
|
|
"""
|
|
Divides without triggering division by zero exception.
|
|
Returns 0 if denominator is 0.
|
|
"""
|
|
try:
|
|
return numerator / denominator
|
|
except ZeroDivisionError:
|
|
return 0
|
|
|
|
|
|
def safe_getattr(obj: object, attr_chain: str, default: Any | None = None) -> object:
|
|
"""
|
|
Safely get the nested attribute from an object.
|
|
|
|
Parameters:
|
|
obj (object): The object from which to retrieve the attribute.
|
|
attr_chain (str): The chain of attributes, separated by dots.
|
|
default: The default value to return if any attribute in the chain does not exist.
|
|
|
|
Returns:
|
|
The value of the nested attribute if it exists, otherwise the default value.
|
|
"""
|
|
attrs = attr_chain.split(".")
|
|
for attr in attrs:
|
|
try:
|
|
obj = getattr(obj, attr)
|
|
except AttributeError:
|
|
return default
|
|
return obj
|
|
|
|
|
|
def truncate(input_string: str, length: int = 30, ellipsis: str = "…") -> str:
|
|
return (
|
|
(f"{input_string[:length-len(ellipsis)]}{ellipsis}")
|
|
if len(input_string) > 30
|
|
else input_string
|
|
)
|
|
|
|
|
|
T = TypeVar("T", str, int, date)
|
|
|
|
|
|
def generate_split_ranges(
|
|
value_list: list[T], split_points: list[T]
|
|
) -> Generator[tuple[T, T], None, None]:
|
|
for x in range(0, len(split_points) + 1):
|
|
if x == 0:
|
|
start = 0
|
|
elif x >= len(split_points):
|
|
start = value_list.index(split_points[x - 1]) + 1
|
|
else:
|
|
start = value_list.index(split_points[x - 1]) + 1
|
|
try:
|
|
end = value_list.index(split_points[x])
|
|
except IndexError:
|
|
end = len(value_list)
|
|
yield (value_list[start], value_list[end - 1])
|
|
|
|
|
|
def format_float_or_int(number: int | float):
|
|
return int(number) if float(number).is_integer() else f"{number:03.2f}"
|