Yeap, we did experiment with this idea yesterday, seems like it is working - but i am still not clearly understand ho)))
Under gunicorn/daphne/uwsgi local deployments showed stable result:
import contextvars
_request = contextvars.ContextVar('request', default=None)
import threading
import logging
import uuid
from django.conf import settings
from django.http import HttpResponse
from lamb.utils import dpath_value, LambRequest
from lamb.utils.transformers import transform_uuid
from lamb.middleware.async_mixin import AsyncMiddlewareMixin
logger = logging.getLogger(__name__)
class LambGRequestMiddleware(AsyncMiddlewareMixin):
"""
Provides storage for the "current" request object, so that code anywhere
in your project can access it, without it having to be passed to that code
from the view.
"""
def _call(self, request) -> HttpResponse:
self.__class__.set_request(request)
response = self.get_response(request)
self.__class__.del_request()
return response
async def _acall(self, request) -> HttpResponse:
self.__class__.set_request(request)
response = await self.get_response(request)
self.__class__.del_request()
return response
@classmethod
def get_request(cls, default=None):
return _request.get()
@classmethod
def set_request(cls, request):
_request.set(request)
@classmethod
def del_request(cls):
_request.set(None)
import asyncio
import logging
from django.http import HttpResponse
__all__ = ['AsyncMiddlewareMixin']
logger = logging.getLogger(__name__)
class AsyncMiddlewareMixin:
sync_capable = True
async_capable = True
def __init__(self, get_response):
if get_response is None:
raise ValueError('get_response must be provided.')
self.get_response = get_response
self._async_check()
super().__init__()
def __repr__(self):
return '<%s get_response=%s>' % (
self.__class__.__qualname__,
getattr(
self.get_response,
'__qualname__',
self.get_response.__class__.__name__,
),
)
def _async_check(self):
"""
If get_response is a coroutine function, turns us into async mode so
a thread is not consumed during a whole request.
"""
if asyncio.iscoroutinefunction(self.get_response):
# Mark the class as async-capable, but do the actual switch
# inside __call__ to avoid swapping out dunder methods
self._is_coroutine = asyncio.coroutines._is_coroutine
def __call__(self, request):
# Exit out to async mode, if needed
if asyncio.iscoroutinefunction(self.get_response):
return self._acall(request)
else:
return self._call(request)
def _call(self, request) -> HttpResponse:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(response)
return response
async def _acall(self, request) -> HttpResponse:
response = await self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(response)
return response
Yeah, thanks for link - had not expect that it would properly work even under sync mode, seems that under sync mode it wraps threads in same scheme, so at least uWSGI runner works well.