diff --git a/CHANGELOG b/CHANGELOG index c90e236..f015b03 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,16 +11,16 @@ future releases, check `milestones`_ and :doc:`/about/vision`. Big refactoring around middleware configuration, API readability and documentation. -- Bugfix #44 - Introduced ``django_downloadview.File``, which patches - ``django.core.files.base.File.__iter__()`` implementation. - See https://code.djangoproject.com/ticket/21321 - - Bugfix #48 - Fixed ``basename`` assertion in ``assert_download_response``: checks ``Content-Disposition`` header. - Bugfix #49 - Fixed ``content`` assertion in ``assert_download_response``: checks only response's ``streaming_content`` attribute. +- Bugfix #60 - ``VirtualFile.__iter__`` uses ``force_bytes()`` to support both + "text-mode" and "binary-mode" content. + See https://code.djangoproject.com/ticket/21321 + - Feature #50 - Introduced ``django_downloadview.DownloadDispatcherMiddleware`` that iterates over a list of configurable download middlewares. Allows to plug several download middlewares with different configurations. @@ -57,7 +57,6 @@ documentation. - Refactoring #53 - Added base classes in ``django_downloadview.middlewares``, such as ``ProxiedDownloadMiddleware``. - - Refactoring #54 - Expose most Python API directly in `django_downloadview` package. Simplifies ``import`` statements in client applications. Splitted nginx module in a package. diff --git a/demo/demoproject/virtual/views.py b/demo/demoproject/virtual/views.py index 9b4461c..8bb7020 100644 --- a/demo/demoproject/virtual/views.py +++ b/demo/demoproject/virtual/views.py @@ -16,7 +16,7 @@ class TextDownloadView(VirtualDownloadView): class StringIODownloadView(VirtualDownloadView): def get_file(self): """Return wrapper on ``StringIO`` object.""" - file_obj = StringIO(u"Hello world!\n".encode('utf-8')) + file_obj = StringIO(u"Hello world!\n") return VirtualFile(file_obj, name='hello-world.txt') diff --git a/django_downloadview/api.py b/django_downloadview/api.py index e357621..fdd70f8 100644 --- a/django_downloadview/api.py +++ b/django_downloadview/api.py @@ -3,8 +3,7 @@ from django_downloadview.io import StringIteratorIO # NoQA from django_downloadview.files import (StorageFile, # NoQA VirtualFile, - HTTPFile, - File) + HTTPFile) from django_downloadview.response import (DownloadResponse, # NoQA ProxiedDownloadResponse) from django_downloadview.middlewares import (BaseDownloadMiddleware, # NoQA diff --git a/django_downloadview/files.py b/django_downloadview/files.py index fb7d3a8..993fb8f 100644 --- a/django_downloadview/files.py +++ b/django_downloadview/files.py @@ -4,40 +4,12 @@ from __future__ import absolute_import from io import BytesIO from urlparse import urlparse -import django.core.files +from django.core.files.base import File from django.utils.encoding import force_bytes import requests -class File(django.core.files.File): - """Patch Django's :meth:`__iter__` implementation. - - See https://code.djangoproject.com/ticket/21321 - - """ - def __iter__(self): - # Iterate over this file-like object by newlines - buffer_ = None - for chunk in self.chunks(): - chunk_buffer = BytesIO(force_bytes(chunk)) - - for line in chunk_buffer: - if buffer_: - line = buffer_ + line - buffer_ = None - - # If this is the end of a line, yield - # otherwise, wait for the next round - if line[-1] in ('\n', '\r'): - yield line - else: - buffer_ = line - - if buffer_ is not None: - yield buffer_ - - class StorageFile(File): """A file in a Django storage. @@ -204,6 +176,33 @@ class VirtualFile(File): size = property(_get_size, _set_size) + def __iter__(self): + """Same as ``File.__iter__()`` but using ``force_bytes()``. + + See https://code.djangoproject.com/ticket/21321 + + """ + # Iterate over this file-like object by newlines + buffer_ = None + for chunk in self.chunks(): + chunk_buffer = BytesIO(force_bytes(chunk)) + + for line in chunk_buffer: + if buffer_: + line = buffer_ + line + buffer_ = None + + # If this is the end of a line, yield + # otherwise, wait for the next round + if line[-1] in ('\n', '\r'): + yield line + else: + buffer_ = line + + if buffer_ is not None: + yield buffer_ + + class HTTPFile(File): """Wrapper for files that live on remote HTTP servers. diff --git a/django_downloadview/tests/api.py b/django_downloadview/tests/api.py index 60c13e8..1d146b7 100644 --- a/django_downloadview/tests/api.py +++ b/django_downloadview/tests/api.py @@ -42,7 +42,6 @@ class APITestCase(unittest.TestCase): 'BaseDownloadView', 'DownloadMixin', # File wrappers: - 'File', 'StorageFile', 'HTTPFile', 'VirtualFile',