| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- from __future__ import annotations
- from collections.abc import Hashable, Iterable
- from typing import TYPE_CHECKING, Any, TypeVar, Union, overload
- if TYPE_CHECKING:
- T = TypeVar("T")
- HashableT = TypeVar("HashableT", bound=Hashable)
- ClassInfo = Union[type[T], tuple[type[T], ...]]
- @overload
- def always_list(obj: Iterable[T], base_type: ClassInfo = ...) -> list[T]: ...
- @overload
- def always_list(obj: T, base_type: ClassInfo = ...) -> list[T]: ...
- def always_list(obj: Any, base_type: Any = (str, bytes)) -> list[T]:
- """Return a guaranteed list of objects from one instance OR an iterable of such items.
- By default, assume the returned list should have string-like elements (`str`/`bytes`).
- Adapted from `more_itertools.always_iterable`, but simplified for internal use. See:
- https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable
- """
- return [obj] if isinstance(obj, base_type) else list(obj)
- def unique_list(iterable: Iterable[HashableT]) -> list[HashableT]:
- """Return a deduplicated list of items from the given iterable, preserving order."""
- # Trick for O(1) uniqueness check that maintains order
- return list(dict.fromkeys(iterable))
- def one(
- iterable: Iterable[T],
- too_short: type[Exception] | Exception | None = None,
- too_long: type[Exception] | Exception | None = None,
- ) -> T:
- """Return the only item in the iterable.
- Note:
- This is intended **only** as an internal helper/convenience function,
- and its implementation is directly adapted from `more_itertools.one`.
- Users needing similar functionality are strongly encouraged to use
- that library instead:
- https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one
- Args:
- iterable: The iterable to get the only item from.
- too_short: Custom exception to raise if the iterable has no items.
- too_long: Custom exception to raise if the iterable has multiple items.
- Raises:
- ValueError or `too_short`: If the iterable has no items.
- ValueError or `too_long`: If the iterable has multiple items.
- """
- # For a general iterable, avoid inadvertently iterating through all values,
- # which may be costly or impossible (e.g. if infinite). Only check that:
- # ... the first item exists
- it = iter(iterable)
- try:
- obj = next(it)
- except StopIteration:
- raise (too_short or ValueError("Expected 1 item in iterable, got 0")) from None
- # ...the second item doesn't
- try:
- _ = next(it)
- except StopIteration:
- return obj
- raise (
- too_long or ValueError("Expected 1 item in iterable, got multiple")
- ) from None
|