Fixed #31056 -- Allowed disabling async-unsafe check with an environment variable.
This commit is contained in:
parent
635a3f8e6e
commit
c90ab30fa1
|
@ -1,5 +1,6 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import functools
|
import functools
|
||||||
|
import os
|
||||||
|
|
||||||
from django.core.exceptions import SynchronousOnlyOperation
|
from django.core.exceptions import SynchronousOnlyOperation
|
||||||
|
|
||||||
|
@ -12,14 +13,15 @@ def async_unsafe(message):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
# Detect a running event loop in this thread.
|
if not os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'):
|
||||||
try:
|
# Detect a running event loop in this thread.
|
||||||
event_loop = asyncio.get_event_loop()
|
try:
|
||||||
except RuntimeError:
|
event_loop = asyncio.get_event_loop()
|
||||||
pass
|
except RuntimeError:
|
||||||
else:
|
pass
|
||||||
if event_loop.is_running():
|
else:
|
||||||
raise SynchronousOnlyOperation(message)
|
if event_loop.is_running():
|
||||||
|
raise SynchronousOnlyOperation(message)
|
||||||
# Pass onwards.
|
# Pass onwards.
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return inner
|
return inner
|
||||||
|
|
|
@ -9,4 +9,7 @@ Django 3.0.1 fixes several bugs in 3.0.
|
||||||
Bugfixes
|
Bugfixes
|
||||||
========
|
========
|
||||||
|
|
||||||
* ...
|
* Fixed a regression in Django 3.0 by restoring the ability to use Django
|
||||||
|
inside Jupyter and other environments that force an async context, by adding
|
||||||
|
and option to disable :ref:`async-safety` mechanism with
|
||||||
|
``DJANGO_ALLOW_ASYNC_UNSAFE`` environment variable (:ticket:`31056`).
|
||||||
|
|
|
@ -309,6 +309,7 @@ ize
|
||||||
JavaScript
|
JavaScript
|
||||||
Jinja
|
Jinja
|
||||||
jQuery
|
jQuery
|
||||||
|
Jupyter
|
||||||
jython
|
jython
|
||||||
Kaplan
|
Kaplan
|
||||||
Kessler
|
Kessler
|
||||||
|
|
|
@ -12,6 +12,8 @@ There is limited support for other parts of the async ecosystem; namely, Django
|
||||||
can natively talk :doc:`ASGI </howto/deployment/asgi/index>`, and some async
|
can natively talk :doc:`ASGI </howto/deployment/asgi/index>`, and some async
|
||||||
safety support.
|
safety support.
|
||||||
|
|
||||||
|
.. _async-safety:
|
||||||
|
|
||||||
Async-safety
|
Async-safety
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -34,3 +36,21 @@ code from an async context; instead, write your code that talks to async-unsafe
|
||||||
in its own, synchronous function, and call that using
|
in its own, synchronous function, and call that using
|
||||||
``asgiref.sync.async_to_sync``, or any other preferred way of running
|
``asgiref.sync.async_to_sync``, or any other preferred way of running
|
||||||
synchronous code in its own thread.
|
synchronous code in its own thread.
|
||||||
|
|
||||||
|
If you are *absolutely* in dire need to run this code from an asynchronous
|
||||||
|
context - for example, it is being forced on you by an external environment,
|
||||||
|
and you are sure there is no chance of it being run concurrently (e.g. you are
|
||||||
|
in a Jupyter_ notebook), then you can disable the warning with the
|
||||||
|
``DJANGO_ALLOW_ASYNC_UNSAFE`` environment variable.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
If you enable this option and there is concurrent access to the
|
||||||
|
async-unsafe parts of Django, you may suffer data loss or corruption. Be
|
||||||
|
very careful and do not use this in production environments.
|
||||||
|
|
||||||
|
If you need to do this from within Python, do that with ``os.environ``::
|
||||||
|
|
||||||
|
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
||||||
|
|
||||||
|
.. _Jupyter: https://jupyter.org/
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from unittest import skipIf
|
from unittest import mock, skipIf
|
||||||
|
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
|
|
||||||
|
@ -39,3 +40,13 @@ class AsyncUnsafeTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(SynchronousOnlyOperation, msg):
|
with self.assertRaisesMessage(SynchronousOnlyOperation, msg):
|
||||||
self.dangerous_method()
|
self.dangerous_method()
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ, {'DJANGO_ALLOW_ASYNC_UNSAFE': 'true'})
|
||||||
|
@async_to_sync
|
||||||
|
async def test_async_unsafe_suppressed(self):
|
||||||
|
# Decorator doesn't trigger check when the environment variable to
|
||||||
|
# suppress it is set.
|
||||||
|
try:
|
||||||
|
self.dangerous_method()
|
||||||
|
except SynchronousOnlyOperation:
|
||||||
|
self.fail('SynchronousOnlyOperation should not be raised.')
|
||||||
|
|
Loading…
Reference in New Issue