Source code for riverine.util

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar

import functools

if TYPE_CHECKING:
    from kithairon.picklists import PickList

T = TypeVar("T")

CACHE_HITS = 0
CACHE_MISSES = 0
CACHE_SKIPS = 0

# Largely replaced by concrete type code.
# def _maybesequence(object_or_sequence: Sequence[T] | T) -> list[T]:
#     if isinstance(object_or_sequence, Sequence):
#         return list(object_or_sequence)
#     return [object_or_sequence]


[docs] def _none_as_empty_string(v: str | None) -> str: return "" if v is None else v
[docs] def _get_picklist_class() -> type[PickList]: try: from kithairon.picklists import PickList # type: ignore return PickList except ImportError as err: if err.name != "kithairon": raise err raise ImportError( "kithairon is required for Echo support, but it is not installed.", name="kithairon", )
__all__ = ( "_none_as_empty_string", "_get_picklist_class", ) _UNSET = object() def maybe_cache_once(fun): """Cache the result of the most recent call whose `_cache_key` is not None. Cache identity is compared by equality on `(_cache_key, args, kwargs)`. An earlier version keyed by `hash(...)` alone, which returned stale data on any hash collision. """ last_key: object = _UNSET last_cache_data = None def inner(*args, _cache_key=None, **kwargs): nonlocal last_key, last_cache_data if _cache_key is None: global CACHE_SKIPS CACHE_SKIPS += 1 return fun(*args, **kwargs, _cache_key=_cache_key) current_key = (_cache_key, args, tuple(sorted(kwargs.items()))) if last_key is not _UNSET and current_key == last_key: global CACHE_HITS CACHE_HITS += 1 return last_cache_data global CACHE_MISSES CACHE_MISSES += 1 data = fun(*args, **kwargs, _cache_key=_cache_key) last_key = current_key last_cache_data = data return data functools.update_wrapper(inner, fun) return inner def gen_random_hash(): import random import string return "".join(random.choices(string.ascii_lowercase + string.digits, k=15))