Index: Lib/socket.py =================================================================== --- Lib/socket.py (revision 56879) +++ Lib/socket.py (working copy) @@ -253,24 +253,31 @@ # XXX More docs def __init__(self, sock, mode, closer): - assert mode in ("r", "w", "rw") + if mode not in ("r", "w", "rw"): + raise ValueError("invalid mode: %r" % mode) io.RawIOBase.__init__(self) self._sock = sock self._mode = mode self._closer = closer + self._reading = "r" in mode + self._writing = "w" in mode closer.makefile_open() def readinto(self, b): + self._checkClosed() + self._checkReadable() return self._sock.recv_into(b) def write(self, b): + self._checkClosed() + self._checkWritable() return self._sock.send(b) def readable(self): - return "r" in self._mode + return self._reading and not self.closed def writable(self): - return "w" in self._mode + return self._writing and not self.closed def fileno(self): return self._sock.fileno() Index: Lib/io.py =================================================================== --- Lib/io.py (revision 56879) +++ Lib/io.py (working copy) @@ -12,13 +12,12 @@ variable are part of the specification. XXX edge cases when switching between reading/writing -XXX need to default buffer size to 1 if isatty() XXX need to support 1 meaning line-buffered -XXX don't use assert to validate input requirements XXX whenever an argument is None, use the default value XXX read/write ops should check readable/writable XXX buffered readinto should work with arbitrary buffer objects XXX use incremental encoder for text output, at least for UTF-16 and UTF-8-SIG +XXX check writable, readable and seekable in appropriate places """ __author__ = ("Guido van Rossum , " @@ -36,7 +35,7 @@ import _fileio import warnings -# XXX Shouldn't we use st_blksize whenever we can? +# open() uses st_blksize whenever we can DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes @@ -87,11 +86,14 @@ binary stream, a buffered binary stream, or a buffered text stream, open for reading and/or writing. """ - # XXX Don't use asserts for these checks; raise TypeError or ValueError - assert isinstance(file, (basestring, int)), repr(file) - assert isinstance(mode, basestring), repr(mode) - assert buffering is None or isinstance(buffering, int), repr(buffering) - assert encoding is None or isinstance(encoding, basestring), repr(encoding) + if not isinstance(file, (basestring, int)): + raise TypeError("invalid file: %r" % file) + if not isinstance(mode, basestring): + raise TypeError("invalid mode: %r" % mode) + if buffering is not None and not isinstance(buffering, int): + raise TypeError("invalid buffering: %r" % buffering) + if encoding is not None and not isinstance(encoding, basestring): + raise TypeError("invalid encoding: %r" % encoding) modes = set(mode) if modes - set("arwb+tU") or len(mode) > len(modes): raise ValueError("invalid mode: %r" % mode) @@ -122,9 +124,10 @@ (updating and "+" or "")) if buffering is None: buffering = -1 + if raw.isatty(): + buffering = 1 if buffering < 0: buffering = DEFAULT_BUFFER_SIZE - # XXX Should default to line buffering if os.isatty(raw.fileno()) try: bs = os.fstat(raw.fileno()).st_blksize except (os.error, AttributeError): @@ -144,9 +147,10 @@ buffer = BufferedRandom(raw, buffering) elif writing or appending: buffer = BufferedWriter(raw, buffering) + elif reading: + buffer = BufferedReader(raw, buffering) else: - assert reading - buffer = BufferedReader(raw, buffering) + raise ValueError("unknown mode: %r" % mode) if binary: buffer.name = file buffer.mode = mode @@ -255,6 +259,14 @@ """ return False + def _checkSeekable(self, msg=None): + """Internal: raise an IOError if file is not seekable + """ + if not self.seekable(): + raise IOError("File or stream is not seekable." + if msg is None else msg) + + def readable(self) -> bool: """readable() -> bool. Return whether object was opened for reading. @@ -262,6 +274,13 @@ """ return False + def _checkReadable(self, msg=None): + """Internal: raise an IOError if file is not readable + """ + if not self.readable(): + raise IOError("File or stream is not readable." + if msg is None else msg) + def writable(self) -> bool: """writable() -> bool. Return whether object was opened for writing. @@ -269,6 +288,13 @@ """ return False + def _checkWritable(self, msg=None): + """Internal: raise an IOError if file is not writable + """ + if not self.writable(): + raise IOError("File or stream is not writable." + if msg is None else msg) + @property def closed(self): """closed: bool. True iff the file has been closed. @@ -277,6 +303,13 @@ """ return self.__closed + def _checkClosed(self, msg=None): + """Internal: raise an ValueError if file is closed + """ + if self.closed: + raise ValueError("I/O operation on closed file." + if msg is None else msg) + ### Context manager ### def __enter__(self) -> "IOBase": # That's a forward reference @@ -303,8 +336,7 @@ Returns False if we don't know. """ - if self.closed: - raise ValueError("isatty() on closed file") + self._checkClosed() return False ### Readline[s] and writelines ### @@ -336,8 +368,7 @@ return res def __iter__(self): - if self.closed: - raise ValueError("__iter__ on closed file") + self._checkClosed() return self def __next__(self): @@ -359,8 +390,7 @@ return lines def writelines(self, lines): - if self.closed: - raise ValueError("write to closed file") + self._checkClosed() for line in lines: self.write(line) @@ -659,7 +689,7 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE): """Create a new buffered reader using the given readable raw IO object. """ - assert raw.readable() + raw._checkReadable() _BufferedIOMixin.__init__(self, raw) self._read_buf = b"" self.buffer_size = buffer_size @@ -742,7 +772,7 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): - assert raw.writable() + raw._checkWritable() _BufferedIOMixin.__init__(self, raw) self.buffer_size = buffer_size self.max_buffer_size = (2*buffer_size @@ -824,8 +854,8 @@ The arguments are two RawIO instances. """ - assert reader.readable() - assert writer.writable() + reader._checkReadable() + writer._checkWritable() self.reader = BufferedReader(reader, buffer_size) self.writer = BufferedWriter(writer, buffer_size, max_buffer_size) @@ -873,7 +903,7 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): - assert raw.seekable() + raw._checkSeekable() BufferedReader.__init__(self, raw, buffer_size) BufferedWriter.__init__(self, raw, buffer_size, max_buffer_size) @@ -1041,7 +1071,8 @@ return decoder def _read_chunk(self): - assert self._decoder is not None + if self._decoder is None: + raise ValueError("no decoder") if not self._telling: readahead = self.buffer.read1(self._CHUNK_SIZE) pending = self._decoder.decode(readahead, not readahead) @@ -1077,7 +1108,8 @@ position = self.buffer.tell() decoder = self._decoder if decoder is None or self._snapshot is None: - assert self._pending == "" + if self._pending: + raise ValueError("pending data") return position decoder_state, readahead, pending = self._snapshot position -= len(readahead) Index: Lib/test/test_io.py =================================================================== --- Lib/test/test_io.py (revision 56879) +++ Lib/test/test_io.py (working copy) @@ -663,11 +663,26 @@ # XXX Tests for open() +class MiscIOTest(unittest.TestCase): + + def testImport__all__(self): + for name in io.__all__: + obj = getattr(io, name, None) + self.assert_(obj is not None, name) + if name == "open": + continue + elif "error" in name.lower(): + self.assert_(issubclass(obj, Exception), name) + else: + self.assert_(issubclass(obj, io.IOBase)) + + def test_main(): test_support.run_unittest(IOTest, BytesIOTest, StringIOTest, BufferedReaderTest, BufferedWriterTest, BufferedRWPairTest, - BufferedRandomTest, TextIOWrapperTest) + BufferedRandomTest, TextIOWrapperTest, + MiscIOTest) if __name__ == "__main__": unittest.main()