2019-04-12 21:15:18 +08:00
|
|
|
import asyncio
|
|
|
|
import functools
|
2019-12-03 04:02:21 +08:00
|
|
|
import os
|
2019-04-12 21:15:18 +08:00
|
|
|
|
|
|
|
from django.core.exceptions import SynchronousOnlyOperation
|
|
|
|
|
|
|
|
|
|
|
|
def async_unsafe(message):
|
|
|
|
"""
|
|
|
|
Decorator to mark functions as async-unsafe. Someone trying to access
|
|
|
|
the function while in an async context will get an error message.
|
|
|
|
"""
|
|
|
|
def decorator(func):
|
|
|
|
@functools.wraps(func)
|
|
|
|
def inner(*args, **kwargs):
|
2019-12-03 04:02:21 +08:00
|
|
|
if not os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'):
|
|
|
|
# Detect a running event loop in this thread.
|
|
|
|
try:
|
2021-05-04 17:29:23 +08:00
|
|
|
asyncio.get_running_loop()
|
2019-12-03 04:02:21 +08:00
|
|
|
except RuntimeError:
|
|
|
|
pass
|
|
|
|
else:
|
2021-05-04 17:29:23 +08:00
|
|
|
raise SynchronousOnlyOperation(message)
|
2019-04-12 21:15:18 +08:00
|
|
|
# Pass onwards.
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
return inner
|
|
|
|
# If the message is actually a function, then be a no-arguments decorator.
|
|
|
|
if callable(message):
|
|
|
|
func = message
|
|
|
|
message = 'You cannot call this from an async context - use a thread or sync_to_async.'
|
|
|
|
return decorator(func)
|
|
|
|
else:
|
|
|
|
return decorator
|