Repository: django-import-export/django-import-export

Issue:
MediaStorage does not respect the read_mode parameter
Which is a problem if one needs to deal with files by using universal newlines mode, for example.



Discussion:
Thanks for reporting. Would you like to submit a PR?

I started on one, but found that the obvious change broke tests elsewhere.

That is really frustrating :( If you open PR with broken tests I would be glad to take a look and fix tests in the next days.


Resolving patch:
diff --git a/import_export/tmp_storages.py b/import_export/tmp_storages.py
--- a/import_export/tmp_storages.py
+++ b/import_export/tmp_storages.py
@@ -79,9 +79,9 @@ def save(self, data, mode=None):
             self.name = uuid4().hex
         default_storage.save(self.get_full_path(), ContentFile(data))
 
-    def read(self, read_mode='r'):
-        with default_storage.open(self.get_full_path()) as file:
-            return file.read()
+    def read(self, read_mode='rb'):
+        with default_storage.open(self.get_full_path(), mode=read_mode) as f:
+            return f.read()
 
     def remove(self):
         default_storage.delete(self.get_full_path())


Test patch:
diff --git a/tests/core/exports/books-mac.csv b/tests/core/exports/books-mac.csv
new file mode 100644
--- /dev/null
+++ b/tests/core/exports/books-mac.csv
@@ -0,0 +1 @@
+id,name,author_email1,Some book,test@example.com
\ No newline at end of file
diff --git a/tests/core/tests/admin_integration_tests.py b/tests/core/tests/admin_integration_tests.py
--- a/tests/core/tests/admin_integration_tests.py
+++ b/tests/core/tests/admin_integration_tests.py
@@ -64,6 +64,40 @@ def test_import(self):
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, _('Import finished'))
 
+    @override_settings(TEMPLATE_STRING_IF_INVALID='INVALID_VARIABLE')
+    def test_import_mac(self):
+        # GET the import form
+        response = self.client.get('/admin/core/book/import/')
+        self.assertEqual(response.status_code, 200)
+        self.assertTemplateUsed(response, 'admin/import_export/import.html')
+        self.assertContains(response, 'form action=""')
+
+        # POST the import form
+        input_format = '0'
+        filename = os.path.join(
+            os.path.dirname(__file__),
+            os.path.pardir,
+            'exports',
+            'books-mac.csv')
+        with open(filename, "rb") as f:
+            data = {
+                'input_format': input_format,
+                'import_file': f,
+            }
+            response = self.client.post('/admin/core/book/import/', data)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn('result', response.context)
+        self.assertFalse(response.context['result'].has_errors())
+        self.assertIn('confirm_form', response.context)
+        confirm_form = response.context['confirm_form']
+
+        data = confirm_form.initial
+        self.assertEqual(data['original_file_name'], 'books-mac.csv')
+        response = self.client.post('/admin/core/book/process_import/', data,
+                                    follow=True)
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, _('Import finished'))
+
     def test_export(self):
         response = self.client.get('/admin/core/book/export/')
         self.assertEqual(response.status_code, 200)
diff --git a/tests/core/tests/base_formats_tests.py b/tests/core/tests/base_formats_tests.py
--- a/tests/core/tests/base_formats_tests.py
+++ b/tests/core/tests/base_formats_tests.py
@@ -52,6 +52,16 @@ def test_import_dos(self):
         expected = 'id,name,author_email\n1,Some book,test@example.com\n'
         self.assertEqual(in_stream, expected)
 
+    def test_import_mac(self):
+        filename = os.path.join(
+            os.path.dirname(__file__),
+            os.path.pardir,
+            'exports',
+            'books-mac.csv')
+        in_stream = open(filename, self.format.get_read_mode()).read()
+        expected = 'id,name,author_email\n1,Some book,test@example.com\n'
+        self.assertEqual(in_stream, expected)
+
     def test_import_unicode(self):
         # importing csv UnicodeEncodeError 347
         filename = os.path.join(
diff --git a/tests/core/tests/tmp_storages_tests.py b/tests/core/tests/tmp_storages_tests.py
--- a/tests/core/tests/tmp_storages_tests.py
+++ b/tests/core/tests/tmp_storages_tests.py
@@ -4,6 +4,8 @@
 from django.test import TestCase
 from django.core.cache import cache
 from django.core.files.storage import default_storage
+from django.utils import six
+
 from import_export.tmp_storages import (
     CacheStorage,
     MediaStorage,
@@ -56,3 +58,16 @@ def test_media_storage(self):
         self.assertTrue(default_storage.exists(tmp_storage.get_full_path()))
         tmp_storage.remove()
         self.assertFalse(default_storage.exists(tmp_storage.get_full_path()))
+
+    def test_media_storage_read_mode(self):
+        # issue 416 - MediaStorage does not respect the read_mode parameter.
+        test_string = self.test_string.replace(b'\n', b'\r')
+
+        tmp_storage = MediaStorage()
+        tmp_storage.save(test_string)
+        name = tmp_storage.name
+
+        tmp_storage = MediaStorage(name=name)
+        read_mode = 'r' if six.PY3 else 'rbU'
+        self.assertEqual(self.test_string.decode('utf-8'),
+                         tmp_storage.read(read_mode=read_mode))