I have a Django view that serves the content of a file. The Django application was running with WSGI until recently. This worked fine. Then I adapted my application to use ASGI running uvicorn. The file serving is now broken as it seems to loose the connection.
How can I serve the file asynchronously with Django and uvicorn?
Current view:
class FileServeView(View): def get(self, request, *args, **kwargs): # ... return HttpResponse( FileWrapper(file_content), content_type="application/octet-stream" )
The server is throwing the following error:
ERROR: Exception in ASGI application Traceback (most recent call last): File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 377, in run_asgi result = await app(self.scope, self.receive, self.send) File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__ return await self.app(scope, receive, send) File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/asgi2.py", line 17, in __call__ await instance(receive, send) File "/usr/local/lib/python3.8/site-packages/channels/http.py", line 192, in __call__ await self.handle(body_stream) File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 382, in __call__ raise RuntimeError( RuntimeError: Single thread executor already being used, would deadlock
I’m using:
Django 3.2.13 uvicorn 0.17.1 channels 2.3.1 asgiref 3.5.0
Advertisement
Answer
I can’t reproduce with your example, but this question seems to relate to your problem: channels#1722.
Check the dr-luk response:
As @fmgoncalves has mentioned, one way is to alter how the files are served, but I have found a little more reliable patch that I have implemented in my Django Channels Setup based on the information provided by their post.
It seems that the change of
thread_sensitive=False
to default toTrue
is causing these deadlock detection messages. As far as I can see, Django Channels doesn’t seem to mind the occurrences of the deadlocks.That being said, I felt it would be safe to monkey patch the
sync_to_async
function for my Django installation.
project_app/moneky_patches.py
from asgiref.sync import sync_to_async def patch_sync_to_async(*args, **kwargs): """ Monkey Patch the sync_to_async decorator --------------------------------------- ASGIRef made a change in their defaults that has caused major problems for channels. The decorator below needs to be updated to use thread_sensitive=False, thats why we are patching it on our side for now. https://github.com/django/channels/blob/main/channels/http.py#L220 """ kwargs['thread_sensitive'] = False return sync_to_async(*args, **kwargs)Then you just overwrite the existing instance of asgiref’s sync_to_async method with our patched wrapper that enforces thread_sensitive=False
project_app/__init__.py
from . import monkey_patches import asgiref ### Monkey Patches asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_asyncThis is make channels, and all of Django run in a insensitive manner like it did before the ASGIRef update.