from inspect import iscoroutinefunction, markcoroutinefunction from asgiref.sync import sync_to_async __all__ = ["MiddlewareMixin"] class MiddlewareMixin: 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 # If get_response is a coroutine function, turns us into async mode so # a thread is not consumed during a whole request. self.async_mode = iscoroutinefunction(self.get_response) if self.async_mode: # Mark the class as async-capable, but do the actual switch inside # __call__ to avoid swapping out dunder methods. markcoroutinefunction(self) super().__init__() def __repr__(self): return "<%s get_response=%s>" % ( self.__class__.__qualname__, getattr( self.get_response, "__qualname__", self.get_response.__class__.__name__, ), ) def __call__(self, request): # Exit out to async mode, if needed if self.async_mode: return self.__acall__(request) response = None if hasattr(self, "process_request"): response = self.process_request(request) response = response or self.get_response(request) if hasattr(self, "process_response"): response = self.process_response(request, response) return response async def __acall__(self, request): """ Async version of __call__ that is swapped in when an async request is running. """ response = None if hasattr(self, "process_request"): response = await sync_to_async( self.process_request, thread_sensitive=True, )(request) response = response or await self.get_response(request) if hasattr(self, "process_response"): response = await sync_to_async( self.process_response, thread_sensitive=True, )(request, response) return response