summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Klöcker <ingokloecker@users.noreply.github.com>2017-04-07 14:21:06 +0200
committerFlorian Apolloner <apollo13@users.noreply.github.com>2017-04-07 14:21:06 +0200
commitc4536c4a54282cd89bc815b58cc3c73280712df1 (patch)
tree3d425bbaaafe0cffd5d7ad19cf1f733fa805d975
parent695d4dd7908ca32e118716b474c23b43727579d2 (diff)
Fixed #27777 -- Made File.open() work with the with statement (#8310)
Fixed #27777 -- Made File.open() work with the with statement
-rw-r--r--AUTHORS1
-rw-r--r--django/core/files/base.py2
-rw-r--r--django/core/files/uploadedfile.py1
-rw-r--r--django/db/models/fields/files.py1
-rw-r--r--docs/ref/files/file.txt3
-rw-r--r--tests/files/tests.py36
6 files changed, 43 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index 0ab2376643..1817ebafa7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -324,6 +324,7 @@ answer newbie questions, and generally made Django that much better:
Igor Kolar <ike@email.si>
Illia Volochii <illia.volochii@gmail.com>
Ilya Semenov <semenov@inetss.com>
+ Ingo Klöcker <djangoproject@ingo-kloecker.de>
I.S. van Oostveen <v.oostveen@idca.nl>
ivan.chelubeev@gmail.com
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
diff --git a/django/core/files/base.py b/django/core/files/base.py
index ff0a289dea..5e6332f0b6 100644
--- a/django/core/files/base.py
+++ b/django/core/files/base.py
@@ -125,6 +125,7 @@ class File(FileProxyMixin):
self.file = open(self.name, mode or self.mode)
else:
raise ValueError("The file cannot be reopened.")
+ return self
def close(self):
self.file.close()
@@ -147,6 +148,7 @@ class ContentFile(File):
def open(self, mode=None):
self.seek(0)
+ return self
def close(self):
pass
diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py
index 485bfbfd6f..60c354a9f7 100644
--- a/django/core/files/uploadedfile.py
+++ b/django/core/files/uploadedfile.py
@@ -85,6 +85,7 @@ class InMemoryUploadedFile(UploadedFile):
def open(self, mode=None):
self.file.seek(0)
+ return self
def chunks(self, chunk_size=None):
self.file.seek(0)
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 92e710299f..cbd0386435 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -75,6 +75,7 @@ class FieldFile(File):
self.file.open(mode)
else:
self.file = self.storage.open(self.name, mode)
+ return self
# open() doesn't alter the file's contents, but it does reset the pointer
open.alters_data = True
diff --git a/docs/ref/files/file.txt b/docs/ref/files/file.txt
index 803f060c29..d79ae57f47 100644
--- a/docs/ref/files/file.txt
+++ b/docs/ref/files/file.txt
@@ -56,6 +56,9 @@ The ``File`` class
was originally opened with; ``None`` means to reopen with the original
mode.
+ Returns ``self``, so that it can be used similar to Python's
+ built-in :func:`python:open()` with the ``with`` statement.
+
.. method:: read(num_bytes=None)
Read content from the file. The optional ``size`` is the number of
diff --git a/tests/files/tests.py b/tests/files/tests.py
index 9b3e019f8d..1a383090f9 100644
--- a/tests/files/tests.py
+++ b/tests/files/tests.py
@@ -10,7 +10,9 @@ from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.move import file_move_safe
from django.core.files.temp import NamedTemporaryFile
-from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
+from django.core.files.uploadedfile import (
+ InMemoryUploadedFile, SimpleUploadedFile, UploadedFile,
+)
try:
from PIL import Image
@@ -38,6 +40,23 @@ class FileTests(unittest.TestCase):
self.assertTrue(f.closed)
self.assertTrue(orig_file.closed)
+ def test_open_resets_opened_file_to_start_and_returns_context_manager(self):
+ file = File(BytesIO(b'content'))
+ file.read()
+ with file.open() as f:
+ self.assertEqual(f.read(), b'content')
+
+ def test_open_reopens_closed_file_and_returns_context_manager(self):
+ temporary_file = tempfile.NamedTemporaryFile(delete=False)
+ file = File(temporary_file)
+ try:
+ file.close()
+ with file.open() as f:
+ self.assertFalse(f.closed)
+ finally:
+ # remove temporary file
+ os.unlink(file.name)
+
def test_namedtemporaryfile_closes(self):
"""
The symbol django.core.files.NamedTemporaryFile is assigned as
@@ -178,6 +197,21 @@ class ContentFileTestCase(unittest.TestCase):
self.assertIsInstance(ContentFile(b"content").read(), bytes)
self.assertIsInstance(ContentFile("español").read(), str)
+ def test_open_resets_file_to_start_and_returns_context_manager(self):
+ file = ContentFile(b'content')
+ with file.open() as f:
+ self.assertEqual(f.read(), b'content')
+ with file.open() as f:
+ self.assertEqual(f.read(), b'content')
+
+
+class InMemoryUploadedFileTests(unittest.TestCase):
+ def test_open_resets_file_to_start_and_returns_context_manager(self):
+ uf = InMemoryUploadedFile(StringIO('1'), '', 'test', 'text/plain', 1, 'utf8')
+ uf.read()
+ with uf.open() as f:
+ self.assertEqual(f.read(), '1')
+
class DimensionClosingBug(unittest.TestCase):
"""