@@ -162,24 +162,30 @@ def __call__(self, request):
162
162
if iscoroutinefunction (self .get_response ):
163
163
return self .acall (request )
164
164
165
- # Force Django >= 3.2 to use async file responses
165
+ # Force Django >= 3.2 use async file responses when using ASGI, even
166
+ # if Django forces this middleware to run synchronously
166
167
if django .VERSION >= (3 , 2 ):
167
168
return asyncio .run (self .acall (request ))
168
169
169
170
# Django version has no async uspport
170
171
return self .call (request )
171
172
172
173
def call (self , request ):
173
- """TODO: This can be deleted once Django 4.2 is the minimum supported version."""
174
+ """If the URL contains a static file, serve it.
175
+ Otherwise, continue to the next middleware."""
174
176
if self .autorefresh :
175
177
static_file = self .find_file (request .path_info )
176
178
else :
177
179
static_file = self .files .get (request .path_info )
178
180
if static_file is not None :
179
181
return self .serve (static_file , request )
182
+
183
+ # Run the next middleware in the stack
180
184
return self .get_response (request )
181
185
182
186
async def acall (self , request ):
187
+ """If the URL contains a static file, serve it.
188
+ Otherwise, continue to the next middleware."""
183
189
if self .autorefresh and hasattr (asyncio , "to_thread" ):
184
190
# Use a thread while searching disk for files on Python 3.9+
185
191
static_file = await asyncio .to_thread (self .find_file , request .path_info )
@@ -189,7 +195,13 @@ async def acall(self, request):
189
195
static_file = self .files .get (request .path_info )
190
196
if static_file is not None :
191
197
return await self .aserve (static_file , request )
192
- return await self .get_response (request )
198
+
199
+ # Run the next middleware in the stack. Note that get_response can sometimes be sync if
200
+ # middleware was run in mixed sync-async mode
201
+ # https://docs.djangoproject.com/en/stable/topics/http/middleware/#asynchronous-support
202
+ if iscoroutinefunction (self .get_response ):
203
+ return await self .get_response (request )
204
+ return self .get_response (request )
193
205
194
206
@staticmethod
195
207
def serve (static_file , request ):
@@ -320,7 +332,11 @@ class AsyncToSyncIterator:
320
332
This converter must create a temporary event loop in a thread for two reasons:
321
333
1) Allows us to stream the iterator instead of buffering all contents in memory.
322
334
2) Allows the iterator to be used in environments where an event loop may not exist,
323
- or may be closed unexpectedly."""
335
+ or may be closed unexpectedly.
336
+
337
+ Currently used to add async file compatibility to Django WSGI and Django versions
338
+ that do not support __aiter__.
339
+ """
324
340
325
341
def __init__ (self , iterator : AsyncIterable ):
326
342
self .iterator = iterator
0 commit comments