File: async_compatibility.rst

package info (click to toggle)
quart 0.20.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,892 kB
  • sloc: python: 8,644; makefile: 42; sh: 17; sql: 6
file content (72 lines) | stat: -rw-r--r-- 2,323 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
.. _async_compatibility:

Async compatibility
===================

Synchronous and asynchronous code are not directly compatible in that
the functions must be called differently depending on the type. This
limits what can be done, for example in how Quart interacts with Flask
extensions and any effort to make Flask directly asynchronous.

In my opinion it is much easier to start with an asynchronous codebase
that calls synchronous code than vice versa in Python. I will try and
reason why below.

Calling sync code from async functions
--------------------------------------

This is mostly easy in that you can either call, or via a simple wrapper
await a synchronous function,

.. code-block:: python

    async def example():
        sync_call()
        await asyncio.coroutine(sync_call)()

whilst this doesn't actually change the nature, the call is
synchronous, it does work.

Calling async code from sync functions
--------------------------------------

This is where things get difficult, as it is only possible to create a
single event loop. Hence this can only be used once,

.. code-block:: python

    def example():
        loop = asyncio.get_event_loop()
        loop.run_until_complete(async_call())

therefore if you are not at the very outer scope it isn't really
possible to call asynchronous code from a synchronous function.

This is problematic when dealing with Flask extensions as for example the
extension may have something like,

.. code-block:: python

    @app.route('/')
    def route():
        data = request.form
        return render_template_string("{{ name }}", name=data['name'])

whilst the route function can be wrapped with the
``asyncio.coroutine`` function and hence awaited, there is no (easy?)
way to insert the ``await`` before the ``request.form`` and
``render_template`` calls.

It is for this reason that Quart-Flask-Patch creates sync wrapped
versions for the Flask extensions. The former adding synchronous
request methods and the other providing synchronous functions.

Quart monkey patches a ``sync_wait`` method onto the base event loop
allowing for definitions such as,

.. code-block:: python

    from quart.templating import render_template as quart_render_template

    def render_template(*args):
        return asyncio.sync_wait(quart_render_template(*args))