Fixed #26646 -- Added IOBase methods required by TextIOWrapper to File.
Thanks Tim for the review.
This commit is contained in:
parent
6ab0d1358f
commit
4f474607de
|
@ -64,10 +64,6 @@ class File(FileProxyMixin):
|
||||||
|
|
||||||
size = property(_get_size, _set_size)
|
size = property(_get_size, _set_size)
|
||||||
|
|
||||||
def _get_closed(self):
|
|
||||||
return not self.file or self.file.closed
|
|
||||||
closed = property(_get_closed)
|
|
||||||
|
|
||||||
def chunks(self, chunk_size=None):
|
def chunks(self, chunk_size=None):
|
||||||
"""
|
"""
|
||||||
Read the file and yield chunks of ``chunk_size`` bytes (defaults to
|
Read the file and yield chunks of ``chunk_size`` bytes (defaults to
|
||||||
|
|
|
@ -58,15 +58,6 @@ if os.name == 'nt':
|
||||||
except (OSError):
|
except (OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
|
||||||
def closed(self):
|
|
||||||
"""
|
|
||||||
This attribute needs to be accessible in certain situations,
|
|
||||||
because this class is supposed to mock the API of the class
|
|
||||||
tempfile.NamedTemporaryFile in the Python standard library.
|
|
||||||
"""
|
|
||||||
return self.file.closed
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
|
|
||||||
class FileProxyMixin(object):
|
class FileProxyMixin(object):
|
||||||
"""
|
"""
|
||||||
A mixin class used to forward file methods to an underlaying file
|
A mixin class used to forward file methods to an underlaying file
|
||||||
|
@ -27,8 +24,31 @@ class FileProxyMixin(object):
|
||||||
write = property(lambda self: self.file.write)
|
write = property(lambda self: self.file.write)
|
||||||
writelines = property(lambda self: self.file.writelines)
|
writelines = property(lambda self: self.file.writelines)
|
||||||
xreadlines = property(lambda self: self.file.xreadlines)
|
xreadlines = property(lambda self: self.file.xreadlines)
|
||||||
if six.PY3:
|
|
||||||
seekable = property(lambda self: self.file.seekable)
|
@property
|
||||||
|
def closed(self):
|
||||||
|
return not self.file or self.file.closed
|
||||||
|
|
||||||
|
def readable(self):
|
||||||
|
if self.closed:
|
||||||
|
return False
|
||||||
|
if hasattr(self.file, 'readable'):
|
||||||
|
return self.file.readable()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def writable(self):
|
||||||
|
if self.closed:
|
||||||
|
return False
|
||||||
|
if hasattr(self.file, 'writable'):
|
||||||
|
return self.file.writable()
|
||||||
|
return 'w' in getattr(self.file, 'mode', '')
|
||||||
|
|
||||||
|
def seekable(self):
|
||||||
|
if self.closed:
|
||||||
|
return False
|
||||||
|
if hasattr(self.file, 'seekable'):
|
||||||
|
return self.file.seekable()
|
||||||
|
return True
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.file)
|
return iter(self.file)
|
||||||
|
|
|
@ -92,8 +92,13 @@ The ``File`` class
|
||||||
the following attributes and methods of its ``file`` object:
|
the following attributes and methods of its ``file`` object:
|
||||||
``encoding``, ``fileno``, ``flush``, ``isatty``, ``newlines``,
|
``encoding``, ``fileno``, ``flush``, ``isatty``, ``newlines``,
|
||||||
``read``, ``readinto``, ``readlines``, ``seek``, ``softspace``, ``tell``,
|
``read``, ``readinto``, ``readlines``, ``seek``, ``softspace``, ``tell``,
|
||||||
``truncate``, ``writelines``, ``xreadlines``. If you are using
|
``truncate``, ``writelines``, ``xreadlines``, ``readable()``,
|
||||||
Python 3, the ``seekable`` method is also available.
|
``writable()``, and ``seekable()``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.11
|
||||||
|
|
||||||
|
The ``readable()`` and ``writable()`` methods were added and the
|
||||||
|
``seekable()`` method was made available on Python 2.
|
||||||
|
|
||||||
.. currentmodule:: django.core.files.base
|
.. currentmodule:: django.core.files.base
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,9 @@ Email
|
||||||
File Storage
|
File Storage
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* To make it wrappable by :class:`io.TextIOWrapper`,
|
||||||
|
:class:`~django.core.files.File` now has the ``readable()``, ``writable()``,
|
||||||
|
and ``seekable()`` methods.
|
||||||
|
|
||||||
File Uploads
|
File Uploads
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
|
@ -972,6 +972,7 @@ wordcount
|
||||||
wordwrap
|
wordwrap
|
||||||
workflow
|
workflow
|
||||||
worksforme
|
worksforme
|
||||||
|
wrappable
|
||||||
wsgi
|
wsgi
|
||||||
www
|
www
|
||||||
xe
|
xe
|
||||||
|
|
|
@ -6,7 +6,7 @@ import os
|
||||||
import struct
|
import struct
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO, TextIOWrapper
|
||||||
|
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
@ -120,18 +120,39 @@ class FileTests(unittest.TestCase):
|
||||||
f = File(StringIO('one\ntwo\nthree'))
|
f = File(StringIO('one\ntwo\nthree'))
|
||||||
self.assertEqual(list(f), ['one\n', 'two\n', 'three'])
|
self.assertEqual(list(f), ['one\n', 'two\n', 'three'])
|
||||||
|
|
||||||
|
def test_readable(self):
|
||||||
|
with tempfile.TemporaryFile() as temp, File(temp, name='something.txt') as test_file:
|
||||||
|
self.assertTrue(test_file.readable())
|
||||||
|
self.assertFalse(test_file.readable())
|
||||||
|
|
||||||
|
def test_writable(self):
|
||||||
|
with tempfile.TemporaryFile() as temp, File(temp, name='something.txt') as test_file:
|
||||||
|
self.assertTrue(test_file.writable())
|
||||||
|
self.assertFalse(test_file.writable())
|
||||||
|
with tempfile.TemporaryFile('rb') as temp, File(temp, name='something.txt') as test_file:
|
||||||
|
self.assertFalse(test_file.writable())
|
||||||
|
|
||||||
def test_seekable(self):
|
def test_seekable(self):
|
||||||
"""
|
with tempfile.TemporaryFile() as temp, File(temp, name='something.txt') as test_file:
|
||||||
File.seekable() should be available on Python 3.
|
self.assertTrue(test_file.seekable())
|
||||||
"""
|
self.assertFalse(test_file.seekable())
|
||||||
with tempfile.TemporaryFile() as temp:
|
|
||||||
temp.write(b"contents\n")
|
def test_io_wrapper(self):
|
||||||
test_file = File(temp, name="something.txt")
|
content = "vive l'été\n"
|
||||||
if six.PY2:
|
with tempfile.TemporaryFile() as temp, File(temp, name='something.txt') as test_file:
|
||||||
self.assertFalse(hasattr(test_file, 'seekable'))
|
test_file.write(content.encode('utf-8'))
|
||||||
if six.PY3:
|
test_file.seek(0)
|
||||||
self.assertTrue(hasattr(test_file, 'seekable'))
|
wrapper = TextIOWrapper(test_file, 'utf-8', newline='\n')
|
||||||
self.assertTrue(test_file.seekable())
|
self.assertEqual(wrapper.read(), content)
|
||||||
|
# The following seek() call is required on Windows Python 2 when
|
||||||
|
# switching from reading to writing.
|
||||||
|
wrapper.seek(0, 2)
|
||||||
|
wrapper.write(content)
|
||||||
|
wrapper.seek(0)
|
||||||
|
self.assertEqual(wrapper.read(), content * 2)
|
||||||
|
test_file = wrapper.detach()
|
||||||
|
test_file.seek(0)
|
||||||
|
self.assertEqual(test_file.read(), (content * 2).encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
class NoNameFileTestCase(unittest.TestCase):
|
class NoNameFileTestCase(unittest.TestCase):
|
||||||
|
|
Loading…
Reference in New Issue