cpython: c27e9dcad1a3 (original) (raw)
Mercurial > cpython
changeset 100808:c27e9dcad1a3
Issue #22854: Merge UnsupportedOperation fixes from 3.5 [#22854]
Martin Panter vadmium+py@gmail.com | |
---|---|
date | Thu, 31 Mar 2016 08:25:59 +0000 |
parents | b6eebe7cf5ae(current diff)dc9e5f09ac0c(diff) |
children | fb10d1f5016e |
files | Lib/_pyio.py Lib/test/test_io.py Misc/NEWS Modules/_io/bufferedio.c Modules/_io/iobase.c |
diffstat | 6 files changed, 130 insertions(+), 35 deletions(-)[+] [-] Lib/_pyio.py 18 Lib/test/test_io.py 116 Misc/NEWS 3 Modules/_io/bufferedio.c 2 Modules/_io/clinic/iobase.c.h 10 Modules/_io/iobase.c 16 |
line wrap: on
line diff
--- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -390,7 +390,7 @@ class IOBase(metaclass=abc.ABCMeta): def seekable(self): """Return a bool indicating whether object supports random access.
If False, seek(), tell() and truncate() will raise UnsupportedOperation.[](#l1.7)
If False, seek(), tell() and truncate() will raise OSError.[](#l1.8) This method may need to do a test seek().[](#l1.9) """[](#l1.10) return False[](#l1.11)
@@ -405,7 +405,7 @@ class IOBase(metaclass=abc.ABCMeta): def readable(self): """Return a bool indicating whether object was opened for reading.
If False, read() will raise UnsupportedOperation.[](#l1.16)
If False, read() will raise OSError.[](#l1.17) """[](#l1.18) return False[](#l1.19)
@@ -419,7 +419,7 @@ class IOBase(metaclass=abc.ABCMeta): def writable(self): """Return a bool indicating whether object was opened for writing.
If False, write() and truncate() will raise UnsupportedOperation.[](#l1.25)
If False, write() and truncate() will raise OSError.[](#l1.26) """[](#l1.27) return False[](#l1.28)
@@ -787,12 +787,6 @@ class _BufferedIOMixin(BufferedIOBase): def seekable(self): return self.raw.seekable()
- @property def raw(self): return self._raw @@ -982,6 +976,9 @@ class BufferedReader(_BufferedIOMixin): self._reset_read_buf() self._read_lock = Lock()
+ def _reset_read_buf(self): self._read_buf = b"" self._read_pos = 0 @@ -1170,6 +1167,9 @@ class BufferedWriter(_BufferedIOMixin): self._write_buf = bytearray() self._write_lock = Lock()
+ def write(self, b): if self.closed: raise ValueError("write to closed file")
--- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -203,6 +203,9 @@ class MockUnseekableIO: def tell(self, *args): raise self.UnsupportedOperation("not seekable")
+ class CMockUnseekableIO(MockUnseekableIO, io.BytesIO): UnsupportedOperation = io.UnsupportedOperation @@ -361,6 +364,107 @@ class IOTest(unittest.TestCase): self.assertRaises(exc, fp.seek, 1, self.SEEK_CUR) self.assertRaises(exc, fp.seek, -1, self.SEEK_END)
- def test_optional_abilities(self):
# Test for OSError when optional APIs are not supported[](#l2.18)
# The purpose of this test is to try fileno(), reading, writing and[](#l2.19)
# seeking operations with various objects that indicate they do not[](#l2.20)
# support these operations.[](#l2.21)
def pipe_reader():[](#l2.23)
[r, w] = os.pipe()[](#l2.24)
os.close(w) # So that read() is harmless[](#l2.25)
return self.FileIO(r, "r")[](#l2.26)
def pipe_writer():[](#l2.28)
[r, w] = os.pipe()[](#l2.29)
self.addCleanup(os.close, r)[](#l2.30)
# Guarantee that we can write into the pipe without blocking[](#l2.31)
thread = threading.Thread(target=os.read, args=(r, 100))[](#l2.32)
thread.start()[](#l2.33)
self.addCleanup(thread.join)[](#l2.34)
return self.FileIO(w, "w")[](#l2.35)
def buffered_reader():[](#l2.37)
return self.BufferedReader(self.MockUnseekableIO())[](#l2.38)
def buffered_writer():[](#l2.40)
return self.BufferedWriter(self.MockUnseekableIO())[](#l2.41)
def buffered_random():[](#l2.43)
return self.BufferedRandom(self.BytesIO())[](#l2.44)
def buffered_rw_pair():[](#l2.46)
return self.BufferedRWPair(self.MockUnseekableIO(),[](#l2.47)
self.MockUnseekableIO())[](#l2.48)
def text_reader():[](#l2.50)
class UnseekableReader(self.MockUnseekableIO):[](#l2.51)
writable = self.BufferedIOBase.writable[](#l2.52)
write = self.BufferedIOBase.write[](#l2.53)
return self.TextIOWrapper(UnseekableReader(), "ascii")[](#l2.54)
def text_writer():[](#l2.56)
class UnseekableWriter(self.MockUnseekableIO):[](#l2.57)
readable = self.BufferedIOBase.readable[](#l2.58)
read = self.BufferedIOBase.read[](#l2.59)
return self.TextIOWrapper(UnseekableWriter(), "ascii")[](#l2.60)
tests = ([](#l2.62)
(pipe_reader, "fr"), (pipe_writer, "fw"),[](#l2.63)
(buffered_reader, "r"), (buffered_writer, "w"),[](#l2.64)
(buffered_random, "rws"), (buffered_rw_pair, "rw"),[](#l2.65)
(text_reader, "r"), (text_writer, "w"),[](#l2.66)
(self.BytesIO, "rws"), (self.StringIO, "rws"),[](#l2.67)
)[](#l2.68)
for [test, abilities] in tests:[](#l2.69)
if test is pipe_writer and not threading:[](#l2.70)
continue # Skip subtest that uses a background thread[](#l2.71)
with self.subTest(test), test() as obj:[](#l2.72)
readable = "r" in abilities[](#l2.73)
self.assertEqual(obj.readable(), readable)[](#l2.74)
writable = "w" in abilities[](#l2.75)
self.assertEqual(obj.writable(), writable)[](#l2.76)
seekable = "s" in abilities[](#l2.77)
self.assertEqual(obj.seekable(), seekable)[](#l2.78)
if isinstance(obj, self.TextIOBase):[](#l2.80)
data = "3"[](#l2.81)
elif isinstance(obj, (self.BufferedIOBase, self.RawIOBase)):[](#l2.82)
data = b"3"[](#l2.83)
else:[](#l2.84)
self.fail("Unknown base class")[](#l2.85)
if "f" in abilities:[](#l2.87)
obj.fileno()[](#l2.88)
else:[](#l2.89)
self.assertRaises(OSError, obj.fileno)[](#l2.90)
if readable:[](#l2.92)
obj.read(1)[](#l2.93)
obj.read()[](#l2.94)
else:[](#l2.95)
self.assertRaises(OSError, obj.read, 1)[](#l2.96)
self.assertRaises(OSError, obj.read)[](#l2.97)
if writable:[](#l2.99)
obj.write(data)[](#l2.100)
else:[](#l2.101)
self.assertRaises(OSError, obj.write, data)[](#l2.102)
if seekable:[](#l2.104)
obj.tell()[](#l2.105)
obj.seek(0)[](#l2.106)
else:[](#l2.107)
self.assertRaises(OSError, obj.tell)[](#l2.108)
self.assertRaises(OSError, obj.seek, 0)[](#l2.109)
if writable and seekable:[](#l2.111)
obj.truncate()[](#l2.112)
obj.truncate(0)[](#l2.113)
else:[](#l2.114)
self.assertRaises(OSError, obj.truncate)[](#l2.115)
self.assertRaises(OSError, obj.truncate, 0)[](#l2.116)
+ def test_open_handles_NUL_chars(self): fn_with_NUL = 'foo\0bar' self.assertRaises(ValueError, self.open, fn_with_NUL, 'w') @@ -751,12 +855,6 @@ class CommonBufferedTests: self.assertEqual(42, bufio.fileno())
- @unittest.skip('test having existential crisis')
- def test_no_fileno(self):
# XXX will we always have fileno() function? If so, kill[](#l2.127)
# this test. Else, write it.[](#l2.128)
pass[](#l2.129)
- def test_invalid_args(self): rawio = self.MockRawIO() bufio = self.tp(rawio) @@ -784,13 +882,9 @@ class CommonBufferedTests: super().flush() rawio = self.MockRawIO() bufio = MyBufferedIO(rawio)
writable = bufio.writable()[](#l2.138) del bufio[](#l2.139) support.gc_collect()[](#l2.140)
if writable:[](#l2.141)
self.assertEqual(record, [1, 2, 3])[](#l2.142)
else:[](#l2.143)
self.assertEqual(record, [1, 2])[](#l2.144)
self.assertEqual(record, [1, 2, 3])[](#l2.145)
def test_context_manager(self): # Test usability as a context manager
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -237,6 +237,9 @@ Core and Builtins Library ------- +- Issue #22854: Change BufferedReader.writable() and
- Issue #26492: Exhausted iterator of array.array now conforms with the behavior of iterators of other mutable sequences: it lefts exhausted even if iterated array is extended.
--- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2398,7 +2398,6 @@ static PyMethodDef bufferedreader_method {"close", (PyCFunction)buffered_close, METH_NOARGS}, {"seekable", (PyCFunction)buffered_seekable, METH_NOARGS}, {"readable", (PyCFunction)buffered_readable, METH_NOARGS},
- {"writable", (PyCFunction)buffered_writable, METH_NOARGS}, {"fileno", (PyCFunction)buffered_fileno, METH_NOARGS}, {"isatty", (PyCFunction)buffered_isatty, METH_NOARGS}, {"_dealloc_warn", (PyCFunction)buffered_dealloc_warn, METH_O}, @@ -2489,7 +2488,6 @@ static PyMethodDef bufferedwriter_method {"close", (PyCFunction)buffered_close, METH_NOARGS}, {"detach", (PyCFunction)buffered_detach, METH_NOARGS}, {"seekable", (PyCFunction)buffered_seekable, METH_NOARGS},
- {"readable", (PyCFunction)buffered_readable, METH_NOARGS}, {"writable", (PyCFunction)buffered_writable, METH_NOARGS}, {"fileno", (PyCFunction)buffered_fileno, METH_NOARGS}, {"isatty", (PyCFunction)buffered_isatty, METH_NOARGS},
--- a/Modules/_io/clinic/iobase.c.h +++ b/Modules/io/clinic/iobase.c.h @@ -66,7 +66,7 @@ PyDoc_STRVAR(io__IOBase_seekable__doc "\n" "Return whether object supports random access.\n" "\n" -"If False, seek(), tell() and truncate() will raise UnsupportedOperation.\n" +"If False, seek(), tell() and truncate() will raise OSError.\n" "This method may need to do a test seek()."); #define IO__IOBASE_SEEKABLE_METHODDEF [](#l5.11) @@ -87,7 +87,7 @@ PyDoc_STRVAR(io__IOBase_readable__doc "\n" "Return whether object was opened for reading.\n" "\n" -"If False, read() will raise UnsupportedOperation."); +"If False, read() will raise OSError."); #define IO__IOBASE_READABLE_METHODDEF [](#l5.19) {"readable", (PyCFunction)io__IOBase_readable, METH_NOARGS, io__IOBase_readable__doc}, @@ -107,7 +107,7 @@ PyDoc_STRVAR(io__IOBase_writable__doc "\n" "Return whether object was opened for writing.\n" "\n" -"If False, write() will raise UnsupportedOperation."); +"If False, write() will raise OSError."); #define IO__IOBASE_WRITABLE_METHODDEF [](#l5.28) {"writable", (PyCFunction)io__IOBase_writable, METH_NOARGS, io__IOBase_writable__doc}, @@ -127,7 +127,7 @@ PyDoc_STRVAR(io__IOBase_fileno__doc, "\n" "Returns underlying file descriptor if one exists.\n" "\n" -"An IOError is raised if the IO object does not use a file descriptor."); +"OSError is raised if the IO object does not use a file descriptor."); #define _IO__IOBASE_FILENO_METHODDEF [](#l5.37) {"fileno", (PyCFunction)io__IOBase_fileno, METH_NOARGS, io__IOBase_fileno__doc}, @@ -276,4 +276,4 @@ static PyObject { return _io__RawIOBase_readall_impl(self); } -/[clinic end generated code: output=fe034152b6884e65 input=a9049054013a1b77]/ +/[clinic end generated code: output=b874952f5cc248a4 input=a9049054013a1b77]*/
--- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -335,13 +335,13 @@ iobase_dealloc(iobase self) Return whether object supports random access. -If False, seek(), tell() and truncate() will raise UnsupportedOperation. +If False, seek(), tell() and truncate() will raise OSError. This method may need to do a test seek(). [clinic start generated code]/ static PyObject _io__IOBase_seekable_impl(PyObject self) -/[clinic end generated code: output=4c24c67f5f32a43d input=22676eebb81dcf1e]/ +/[clinic end generated code: output=4c24c67f5f32a43d input=b976622f7fdf3063]/ { Py_RETURN_FALSE; } @@ -368,12 +368,12 @@ PyObject Return whether object was opened for reading. -If False, read() will raise UnsupportedOperation. +If False, read() will raise OSError. [clinic start generated code]/ static PyObject _io__IOBase_readable_impl(PyObject self) -/[clinic end generated code: output=e48089250686388b input=12fc3d8f6be46434]/ +/[clinic end generated code: output=e48089250686388b input=285b3b866a0ec35f]/ { Py_RETURN_FALSE; } @@ -401,12 +401,12 @@ PyObject Return whether object was opened for writing. -If False, write() will raise UnsupportedOperation. +If False, write() will raise OSError. [clinic start generated code]/ static PyObject _io__IOBase_writable_impl(PyObject self) -/[clinic end generated code: output=406001d0985be14f input=c17a0bb6a8dfc590]/ +/[clinic end generated code: output=406001d0985be14f input=9dcac18a013a05b5]/ { Py_RETURN_FALSE; } @@ -456,12 +456,12 @@ iobase_exit(PyObject self, PyObject ar Returns underlying file descriptor if one exists. -An IOError is raised if the IO object does not use a file descriptor. +OSError is raised if the IO object does not use a file descriptor. [clinic start generated code]/ static PyObject _io__IOBase_fileno_impl(PyObject self) -/[clinic end generated code: output=7cc0973f0f5f3b73 input=32773c5df4b7eede]/ +/[clinic end generated code: output=7cc0973f0f5f3b73 input=4e37028947dc1cc8]*/ { return iobase_unsupported("fileno"); }