# SPDX-FileCopyrightText: 2022-present deepset GmbH # # SPDX-License-Identifier: Apache-2.0 import inspect from typing import Any, TypeVar, Union, get_origin from haystack.utils.type_serialization import _is_union_type T = TypeVar("T") def _is_valid_type(obj: Any) -> bool: """ Check if an object is a valid type annotation. Valid types include: - Normal classes (str, dict, CustomClass) - Generic types (list[str], dict[str, int]) - Union types (Union[str, int], Optional[str], str | int, str | None) :param obj: The object to check :return: True if the object is a valid type annotation, False otherwise Example usage: # >> _is_valid_type(str) # >> True # >> _is_valid_type(list[int]) # >> True # >> _is_valid_type(Union[str, int]) # >> True # >> _is_valid_type(str | int) # >> True # >> _is_valid_type(42) # >> False """ # Handle Union types (including Optional) if (origin := get_origin(obj)) and _is_union_type(origin): return True # Bare Union type (without parameters) is not a valid type annotation # Previously handled by inspect.isclass(obj) but in python 3.14 this returns True for typing.Union if obj == Union: return False # Handle normal classes and generic types return inspect.isclass(obj) or type(obj).__name__ in {"_GenericAlias", "GenericAlias"} def _is_list_type(type_hint: Any) -> bool: """ Check if a type hint represents a list type. :param type_hint: The type hint to check :return: True if the type hint represents a list, False otherwise """ return type_hint == list or (hasattr(type_hint, "__origin__") and get_origin(type_hint) == list) def merge_lists(current: Union[list[T], T, None], new: Union[list[T], T]) -> list[T]: """ Merges two values into a single list. If either `current` or `new` is not already a list, it is converted into one. The function ensures that both inputs are treated as lists and concatenates them. If `current` is None, it is treated as an empty list. :param current: The existing value(s), either a single item or a list. :param new: The new value(s) to merge, either a single item or a list. :return: A list containing elements from both `current` and `new`. """ current_list = [] if current is None else current if isinstance(current, list) else [current] new_list = new if isinstance(new, list) else [new] return current_list + new_list def replace_values(current: Any, new: Any) -> Any: # noqa: ARG001 """ Replace the `current` value with the `new` value. :param current: The existing value :param new: The new value to replace :return: The new value """ return new