Optional Synchronous Interface to Asynchronous Functions
<p>I'm writing a library which is using Tornado Web's <code>tornado.httpclient.AsyncHTTPClient</code> to make requests which gives my code a <code>async</code> interface of:</p>

<pre><code>async def my_library_function():
    return await ...
</code></pre>

<p>I want to make this interface optionally serial if the user provides a kwarg - something like: <code>serial=True</code>. Though you can't obviously call a function defined with the <code>async</code> keyword from a normal function without <code>await</code>. This would be ideal - though almost certain imposible in the language at the moment:</p>

<pre><code>async def here_we_go():
    result = await my_library_function()
    result = my_library_function(serial=True)
</code></pre>

<p>I'm not been able to find anything online where someones come up with a nice solution to this. I don't want to have to reimplement basically the same code without the <code>awaits</code> splattered throughout.</p>

<p>Is this something that can be solved or would it need support from the language?</p>

<hr>

<h1>Solution (though use Jesse's instead - explained below)</h1>

<p>Jesse's solution below is pretty much what I'm going to go with. I did end up getting the interface I originally wanted by using a decorator. Something like this:</p>

<pre><code>import asyncio
from functools import wraps


def serializable(f):
    @wraps(f)
    def wrapper(*args, asynchronous=False, **kwargs):
        if asynchronous:
            return f(*args, **kwargs)
        else:
            # Get pythons current execution thread and use that
            loop = asyncio.get_event_loop()
            return loop.run_until_complete(f(*args, **kwargs))
    return wrapper
</code></pre>

<p>This gives you this interface:</p>

<pre><code>result = await my_library_function(asynchronous=True)
result = my_library_function(asynchronous=False)
</code></pre>

<p>I sanity checked this on python's async mailing list and I was lucky enough to have Guido respond and he politely shot it down for this reason:</p>

<blockquote>
  <p>Code smell -- being able to call the same function both asynchronously 
  and synchronously is highly surprising. Also it violates the rule of 
  thumb that the value of an argument shouldn't affect the return type.</p>
</blockquote>

<p>Nice to know it's possible though if not considered a great interface. Guido essentially suggested Jesse's answer and introducing the wrapping function as a helper util in the library instead of hiding it in a decorator.</p>

<p>When you want to call such a function synchronously, use <a href="https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_until_complete" rel="nofollow">run_until_complete</a>:</p>

<pre><code>asyncio.get_event_loop().run_until_complete(here_we_go())
</code></pre>

<p>Of course, if you do this often in your code, you should come up with an abbreviation for this statement, perhaps just:</p>

<pre><code>def sync(fn, *args, **kwargs):
    return asyncio.get_event_loop().run_until_complete(fn(*args, **kwargs))
</code></pre>

<p>Then you could do:</p>

<pre><code>result = sync(here_we_go)
</code></pre>

python asynchronous tornado synchronous