async.txt 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. ====================
  2. Asynchronous support
  3. ====================
  4. .. versionadded:: 3.0
  5. .. currentmodule:: asgiref.sync
  6. Django has developing support for asynchronous ("async") Python, but does not
  7. yet support asynchronous views or middleware; they will be coming in a future
  8. release.
  9. There is limited support for other parts of the async ecosystem; namely, Django
  10. can natively talk :doc:`ASGI </howto/deployment/asgi/index>`, and some async
  11. safety support.
  12. .. _async-safety:
  13. Async-safety
  14. ============
  15. Certain key parts of Django are not able to operate safely in an asynchronous
  16. environment, as they have global state that is not coroutine-aware. These parts
  17. of Django are classified as "async-unsafe", and are protected from execution in
  18. an asynchronous environment. The ORM is the main example, but there are other
  19. parts that are also protected in this way.
  20. If you try to run any of these parts from a thread where there is a *running
  21. event loop*, you will get a
  22. :exc:`~django.core.exceptions.SynchronousOnlyOperation` error. Note that you
  23. don't have to be inside an async function directly to have this error occur. If
  24. you have called a synchronous function directly from an asynchronous function
  25. without going through something like :func:`sync_to_async` or a threadpool,
  26. then it can also occur, as your code is still running in an asynchronous
  27. context.
  28. If you encounter this error, you should fix your code to not call the offending
  29. code from an async context; instead, write your code that talks to async-unsafe
  30. in its own, synchronous function, and call that using
  31. :func:`asgiref.sync.sync_to_async`, or any other preferred way of running
  32. synchronous code in its own thread.
  33. If you are *absolutely* in dire need to run this code from an asynchronous
  34. context - for example, it is being forced on you by an external environment,
  35. and you are sure there is no chance of it being run concurrently (e.g. you are
  36. in a Jupyter_ notebook), then you can disable the warning with the
  37. ``DJANGO_ALLOW_ASYNC_UNSAFE`` environment variable.
  38. .. warning::
  39. If you enable this option and there is concurrent access to the
  40. async-unsafe parts of Django, you may suffer data loss or corruption. Be
  41. very careful and do not use this in production environments.
  42. If you need to do this from within Python, do that with ``os.environ``::
  43. os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
  44. .. _Jupyter: https://jupyter.org/
  45. Async adapter functions
  46. =======================
  47. It is necessary to adapt the calling style when calling synchronous code from
  48. an asynchronous context, or vice-versa. For this there are two adapter
  49. functions, made available from the ``asgiref.sync`` package:
  50. :func:`async_to_sync` and :func:`sync_to_async`. They are used to transition
  51. between sync and async calling styles while preserving compatibility.
  52. These adapter functions are widely used in Django. The `asgiref`_ package
  53. itself is part of the Django project, and it is automatically installed as a
  54. dependency when you install Django with ``pip``.
  55. .. _asgiref: https://pypi.org/project/asgiref/
  56. ``async_to_sync()``
  57. -------------------
  58. .. function:: async_to_sync(async_function, force_new_loop=False)
  59. Wraps an asynchronous function and returns a synchronous function in its place.
  60. Can be used as either a direct wrapper or a decorator::
  61. from asgiref.sync import async_to_sync
  62. sync_function = async_to_sync(async_function)
  63. @async_to_sync
  64. async def async_function(...):
  65. ...
  66. The asynchronous function is run in the event loop for the current thread, if
  67. one is present. If there is no current event loop, a new event loop is spun up
  68. specifically for the async function and shut down again once it completes. In
  69. either situation, the async function will execute on a different thread to the
  70. calling code.
  71. Threadlocals and contextvars values are preserved across the boundary in both
  72. directions.
  73. :func:`async_to_sync` is essentially a more powerful version of the
  74. :py:func:`asyncio.run` function available in Python's standard library. As well
  75. as ensuring threadlocals work, it also enables the ``thread_sensitive`` mode of
  76. :func:`sync_to_async` when that wrapper is used below it.
  77. ``sync_to_async()``
  78. -------------------
  79. .. function:: sync_to_async(sync_function, thread_sensitive=False)
  80. Wraps a synchronous function and returns an asynchronous (awaitable) function
  81. in its place. Can be used as either a direct wrapper or a decorator::
  82. from asgiref.sync import sync_to_async
  83. async_function = sync_to_async(sync_function)
  84. async_function = sync_to_async(sensitive_sync_function, thread_sensitive=True)
  85. @sync_to_async
  86. def sync_function(...):
  87. ...
  88. @sync_to_async(thread_sensitive=True)
  89. def sensitive_sync_function(...):
  90. ...
  91. Threadlocals and contextvars values are preserved across the boundary in both
  92. directions.
  93. Synchronous functions tend to be written assuming they all run in the main
  94. thread, so :func:`sync_to_async` has two threading modes:
  95. * ``thread_sensitive=False`` (the default): the synchronous function will run
  96. in a brand new thread which is then closed once it completes.
  97. * ``thread_sensitive=True``: the synchronous function will run in the same
  98. thread as all other ``thread_sensitive`` functions, and this will be the main
  99. thread, if the main thread is synchronous and you are using the
  100. :func:`async_to_sync` wrapper.
  101. Thread-sensitive mode is quite special, and does a lot of work to run all
  102. functions in the same thread. Note, though, that it *relies on usage of*
  103. :func:`async_to_sync` *above it in the stack* to correctly run things on the
  104. main thread. If you use ``asyncio.run()`` (or other options instead), it will
  105. fall back to just running thread-sensitive functions in a single, shared thread
  106. (but not the main thread).
  107. The reason this is needed in Django is that many libraries, specifically
  108. database adapters, require that they are accessed in the same thread that they
  109. were created in, and a lot of existing Django code assumes it all runs in the
  110. same thread (e.g. middleware adding things to a request for later use by a
  111. view).
  112. Rather than introduce potential compatibility issues with this code, we instead
  113. opted to add this mode so that all existing Django synchronous code runs in the
  114. same thread and thus is fully compatible with asynchronous mode. Note, that
  115. synchronous code will always be in a *different* thread to any async code that
  116. is calling it, so you should avoid passing raw database handles or other
  117. thread-sensitive references around in any new code you write.