# HG changeset patch # Parent 154ae3af03173ce0f735b6dd47a37da97910f0b9 Document that “io” methods should accept bytes-like objects as arguments This matches the usage of ZipFile and BufferedWriter. This still requires return values to be bytes() objects. diff -r 154ae3af0317 Doc/library/io.rst --- a/Doc/library/io.rst Fri Jan 09 16:40:38 2015 -0600 +++ b/Doc/library/io.rst Sat Jan 10 07:33:24 2015 +0000 @@ -66,7 +66,8 @@ Binary I/O ^^^^^^^^^^ -Binary I/O (also called *buffered I/O*) expects and produces :class:`bytes` +Binary I/O (also called *buffered I/O*) expects +:term:`bytes-like objects ` and produces :class:`bytes` objects. No encoding, decoding, or newline translation is performed. This category of streams can be used for all kinds of non-text data, and also when manual control over the handling of text data is desired. @@ -227,8 +228,9 @@ when operations they do not support are called. The basic type used for binary data read from or written to a file is - :class:`bytes`. :class:`bytearray`\s are accepted too, and in some cases - (such as :meth:`readinto`) required. Text I/O classes work with + :class:`bytes`. Other :term:`bytes-like objects ` are + accepted too. In some cases, such as :meth:`~RawIOBase.readinto`, + :class:`bytearray` is required. Text I/O classes work with :class:`str` data. Note that calling any method (even inquiries) on a closed stream is @@ -396,7 +398,7 @@ .. method:: write(b) - Write the given :class:`bytes` or :class:`bytearray` object, *b*, to the + Write the given :term:`bytes-like object`, *b*, to the underlying raw stream and return the number of bytes written. This can be less than ``len(b)``, depending on specifics of the underlying raw stream, and especially if it is in non-blocking mode. ``None`` is @@ -495,7 +497,7 @@ .. method:: write(b) - Write the given :class:`bytes` or :class:`bytearray` object, *b* and + Write the given :term:`bytes-like object`, *b*, and return the number of bytes written (never less than ``len(b)``, since if the write fails an :exc:`OSError` will be raised). Depending on the actual implementation, these bytes may be readily written to the @@ -580,7 +582,8 @@ A stream implementation using an in-memory bytes buffer. It inherits :class:`BufferedIOBase`. - The argument *initial_bytes* contains optional initial :class:`bytes` data. + The argument *initial_bytes* contains + optional initial :term:`bytes-like object` data. :class:`BytesIO` provides or overrides these methods in addition to those from :class:`BufferedIOBase` and :class:`IOBase`: @@ -677,7 +680,7 @@ .. method:: write(b) - Write the :class:`bytes` or :class:`bytearray` object, *b* and return the + Write the :term:`bytes-like object` object, *b*, and return the number of bytes written. When in non-blocking mode, a :exc:`BlockingIOError` is raised if the buffer needs to be written out but the raw stream blocks. diff -r 154ae3af0317 Lib/_pyio.py --- a/Lib/_pyio.py Fri Jan 09 16:40:38 2015 -0600 +++ b/Lib/_pyio.py Sat Jan 10 07:33:24 2015 +0000 @@ -290,8 +290,8 @@ called. The basic type used for binary data read from or written to a file is - bytes. bytearrays are accepted too, and in some cases (such as - readinto) needed. Text I/O classes work with str data. + bytes. Other bytes-like objects are accepted too. In some cases (such as + readinto), bytearray is required. Text I/O classes work with str data. Note that calling any method (even inquiries) on a closed stream is undefined. Implementations may raise OSError in this case. @@ -878,7 +878,8 @@ raise ValueError("write to closed file") if isinstance(b, str): raise TypeError("can't write str to binary stream") - n = len(b) + with memoryview(b) as view: + n = view.nbytes # Size of any bytes-like object if n == 0: return 0 pos = self._pos diff -r 154ae3af0317 Lib/test/test_io.py --- a/Lib/test/test_io.py Fri Jan 09 16:40:38 2015 -0600 +++ b/Lib/test/test_io.py Sat Jan 10 07:33:24 2015 +0000 @@ -528,10 +528,22 @@ def test_array_writes(self): a = array.array('i', range(10)) n = len(a.tobytes()) - with self.open(support.TESTFN, "wb", 0) as f: - self.assertEqual(f.write(a), n) - with self.open(support.TESTFN, "wb") as f: - self.assertEqual(f.write(a), n) + def make_file_writer(): + return self.FileIO(support.TESTFN, "w") + def make_buffered_writer(): + return self.BufferedWriter(self.MockRawIO()) + def make_buffered_random(): + return self.BufferedRandom(self.MockRawIO()) + def make_buffered_rw_pair(): + return self.BufferedRWPair(self.MockRawIO(), self.MockRawIO()) + writer_factories = ( + self.BytesIO, make_file_writer, make_buffered_writer, + make_buffered_random, make_buffered_rw_pair, + ) + for factory in writer_factories: + with self.subTest(factory), factory() as f: + self.assertEqual(f.write(a), n) + f.writelines((a,)) def test_closefd(self): self.assertRaises(ValueError, self.open, support.TESTFN, 'w', diff -r 154ae3af0317 Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py Fri Jan 09 16:40:38 2015 -0600 +++ b/Lib/test/test_memoryio.py Sat Jan 10 07:33:24 2015 +0000 @@ -408,6 +408,15 @@ support.gc_collect() memio.truncate() + def test_bytes_array(self): + buf = b"1234567890" + import array + a = array.array('b', list(buf)) + memio = self.ioclass(a) + self.assertEqual(memio.getvalue(), buf) + self.assertEqual(memio.write(a), 10) + self.assertEqual(memio.getvalue(), buf) + class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, BytesIOMixin, unittest.TestCase): @@ -483,15 +492,6 @@ self.assertRaises(TypeError, memio.write, "1234567890") self.assertRaises(TypeError, memio.writelines, ["1234567890"]) - def test_bytes_array(self): - buf = b"1234567890" - import array - a = array.array('b', list(buf)) - memio = self.ioclass(a) - self.assertEqual(memio.getvalue(), buf) - self.assertEqual(memio.write(a), 10) - self.assertEqual(memio.getvalue(), buf) - def test_issue5449(self): buf = self.buftype("1234567890") self.ioclass(initial_bytes=buf) diff -r 154ae3af0317 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Fri Jan 09 16:40:38 2015 -0600 +++ b/Modules/_io/fileio.c Sat Jan 10 07:33:24 2015 +0000 @@ -1118,7 +1118,8 @@ "or None if no data is available. On end-of-file, returns ''."); PyDoc_STRVAR(write_doc, -"write(b: bytes) -> int. Write bytes b to file, return number written.\n" +"write(b) -> int. Write bytes-like object b to file; return number of bytes\n" +"written.\n" "\n" "Only makes one system call, so not all of the data may be written.\n" "The number of bytes actually written is returned.");