uWSGI File Wrapper and Python 3.5
A couple of weeks ago we hit a small but hard to track issue in one of our clients. They opened an issue telling us that the downloaded files appeared as corrupt. After closer examination, the downloaded files had 0 bytes. The HTTP response was a solid 200 OK with a 0 bytes file attachment.
This was very strange, given that no automatic error reports had been triggered and there was clearly an error. Moreover we noticed that this issue in particular happened whenever the backend Django app returned a file in the response.
This is quite rare in a production environment, given that you most certainly don’t want the Django app handling files, but rather links to the files. However, one usual exception for this rule are small dynamic reports (for bigger ones you’ll want to send an email link when its ready).
On the Django logs we could clearly see that the reports were properly created and no errors were present neither there nor in the NGINX logs.
That left us to take a peek at the application server logs, in this case, uWSGI. It was there that we got to see the following:
io.UnsupportedOperation: fileno During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.5/site-packages/django/core/handlers/wsgi.py", line 199, in __call__ response = environ['wsgi.file_wrapper'](response.file_to_stream) SystemError: returned a result with an error set
After Googling around a bit we found a great explanation of the issue plus a fix at http://beenje.github.io/blog/posts/uwsgi-send_file-and-python35/.
We had updated from Python 3.4 to Python 3.5 in a previous deploy, but no errors had been detected so far because no reports were needed until the end of that month 🙂
See related posts
Clean Code, Technical Debt, and Documentation in Python
Best practices for writing clean, orderly, well-structured Python code that respects the principles of readability, understandability, and respect for standards
From Python to Go or Rust?
Nowadays there are plenty of options for building backend systems. In this post we talk about the one we use in Octobot: Python/Django.