Fundamentally, the sync wrapper has to ask the event loop to wait for the async function to complete.
Assuming it's possible, one way is to create a new event loop in a thread. This means you're creating and tearing down threads for a "trivial" wrapper. And, really, because threads are expensive, you'd probably want to let the caller provide an event loop.
Really, you wind up asking the caller to tell you what event loop to use. Let's compare real async vs. the sync wrapper function:
async func main() {
await middle()
}
async func middle() {
await libraryFunc()
}
# Using the sync wrapper without hiding the loop.
func main() {
loop = getLoop()
middle(loop)
}
func middle(loop) {
libraryFuncWrapper(loop)
}
You wind up "coloring" your functions through a parameter instead of by marking them "async".
Perhaps your async implementation lets you use a globally defined event loop, but, really, globals are just backdoor parameters.
None of that means that such a wrapper is never going to be a good solution. Grabbing the global event loop probably works for many use cases. It's just that it doesn't avoid the coloring problem, it only hides it.