# Copyright 2018-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

import functools

from portage.proxy.objectproxy import ObjectProxy


def _sync_decorator(func, loop=None):
    """
    Decorate an asynchronous function (either a corouting function or a
    function that returns a Future) with a wrapper that runs the function
    synchronously.
    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        from portage.util.futures import asyncio

        return (loop or asyncio.get_event_loop()).run_until_complete(
            func(*args, **kwargs)
        )

    return wrapper


def _sync_methods(obj, loop=None):
    """
    For use with synchronous code that needs to interact with an object
    that has coroutine methods, this function generates a proxy which
    conveniently converts coroutine methods into synchronous methods.
    This allows coroutines to smoothly blend with synchronous
    code, eliminating clutter that might otherwise discourage the
    proliferation of coroutine usage for I/O bound tasks.
    """
    from portage.util.futures import asyncio

    loop = asyncio._wrap_loop(loop)
    return _ObjectAttrWrapper(
        obj,
        lambda attr: (
            _sync_decorator(attr, loop=loop)
            if asyncio.iscoroutinefunction(attr)
            else attr
        ),
    )


class _ObjectAttrWrapper(ObjectProxy):
    __slots__ = ("_obj", "_attr_wrapper")

    def __init__(self, obj, attr_wrapper):
        object.__setattr__(self, "_obj", obj)
        object.__setattr__(self, "_attr_wrapper", attr_wrapper)

    def __getattribute__(self, attr):
        obj = object.__getattribute__(self, "_obj")
        attr_wrapper = object.__getattribute__(self, "_attr_wrapper")
        return attr_wrapper(getattr(obj, attr))

    def _get_target(self):
        return object.__getattribute__(self, "_obj")
