def sync(func: CallableMaybeAwaitable[A, T], *args, **kwargs) -> T:
    """
    Get result of calling function on the given args. If it is awaitable, will
    block until it is finished. Runs in a new thread in such cases.
    """

    if python_utils.is_really_coroutinefunction(func):
        func: Callable[[A], Awaitable[T]]
        awaitable: Awaitable[T] = func(*args, **kwargs)

        # HACK010: Debugging here to make sure it is awaitable.
        assert inspect.isawaitable(awaitable)

        # Check if there is a running loop.
        try:
            loop = asyncio.get_running_loop()

        except Exception:
            # If not, we can create one here and run it until completion.
            loop = asyncio.new_event_loop()
            ret = loop.run_until_complete(awaitable)
            loop.close()
            return ret

        try:
            # If have nest_asyncio, can run in current thread.
            return loop.run_until_complete(awaitable)
        except Exception:
            pass

        # Otherwise we cannot run a new loop in this thread so we create a
        # new thread to run the awaitable until completion.

        def run_in_new_loop():
            th: threading_utils.Thread = current_thread()
            # Attach return value and possibly exception to thread object so we
            # can retrieve from the starter of the thread.
            th.ret = None
            th.error = None
            try:
                loop = asyncio.new_event_loop()
                th.ret = loop.run_until_complete(awaitable)
                loop.close()

            except Exception as e:
                th.error = e

        thread = threading_utils.Thread(target=run_in_new_loop)

        # Start thread and wait for it to finish.
        thread.start()
        thread.join()

        # Get the return or error, return the return or raise the error.
        if thread.error is not None:
            raise thread.error
        else:
            return thread.ret

    else:
        func: Callable[[A], T]
        # Not a coroutine function, so do not need to sync anything.
        # HACK010: TODO: What if the inspect fails here too? We do some checks
        # in desync but not here.

        return func(*args, **kwargs)

    Get result of calling function on the given args. If it is awaitable, will
    block until it is finished. Runs in a new thread in such cases.
    
src/core/trulens/core/utils/asynchro.py