# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Typing utilities."""

from collections.abc import Callable
from typing import Any, ParamSpec, TypeVar, cast


# See https://stackoverflow.com/questions/71253495/how-to-annotate-the-type-of-arguments-forwarded-to-another-function  # noqa: E501
def copy_signature_from[T, **P](
    _origin: Callable[P, T],
) -> Callable[[Callable[..., Any]], Callable[P, T]]:
    """Copy function signature from another one."""

    def decorator(target: Callable[..., Any]) -> Callable[P, T]:
        return cast(Callable[P, T], target)

    return decorator


# This return type isn't very accurate, but this function is only used at
# run-time and not when type-checking anyway, so it doesn't matter much.
def patch_runtime_generic_support[T: type](real_class: T) -> type:
    """
    Patch run-time support for generic types into a class.

    Several libraries have type stubs that define generic classes even
    though their run-time equivalents do not support the necessary special
    method.  Code using these classes can work around this as follows::

        if TYPE_CHECKING:
            SomeClassBase: TypeAlias = SomeClass
        else:
            # Some classes don't support generic types at run-time yet.
            SomeClassBase = patch_runtime_generic_support(SomeClass)
    """

    class PatchRuntimeGenericSupport:
        def __class_getitem__(
            cls, *args: TypeVar | ParamSpec | tuple[TypeVar | ParamSpec, ...]
        ) -> type[T]:
            return real_class

    return PatchRuntimeGenericSupport
