from typing import Callable
from typing import Any
from functools import WRAPPER_ASSIGNMENTS
from functools import WRAPPER_UPDATES


def lazy_wraps(wrapped: Callable) -> Callable:
    """
    This is the equivalent of the @wraps decorator of functools except it won't
    try to grabs attributes of the targeted function on decoration but on access.

    This is needed because of logilab.common.modutils.LazyObject.

    Indeed: if you try to decorate a LazyObject with @wraps, wraps will try to
    access attributes of LazyObject and this will trigger the attempt to import
    the module decorated by LazyObject which you don't want to do when you just
    want to mark this LazyObject has been a deprecated objet that you only
    wants to trigger if the user try to use it.

    Usage: like @wraps()

    >>> @lazy_wraps(function)
    >>> def wrapper(*args, **kwargs): ...
    """

    def update_wrapper_attributes(wrapper: Callable) -> Callable:
        def __getattribute__(self, attribute: str) -> Any:
            if attribute in WRAPPER_ASSIGNMENTS:
                return getattr(wrapped, attribute)

            return super(self.__class__, self).__getattribute__(attribute)

        wrapper.__getattribute__ = __getattribute__  # type: ignore

        for attribute in WRAPPER_UPDATES:
            getattr(wrapper, attribute).update(getattr(wrapped, attribute, {}))

        wrapper.__wrapped__ = wrapped  # type: ignore

        return wrapper

    return update_wrapper_attributes