The patch improves io.py and socket.py's SocketIO: * I've removed all asserts and replaces them by explict raises * I've added four convenient methods _check_readable, _check_writable, _check_seekable and _check_closed. The methods take an optional msg argument for future usage. * unit tests for the stdin.name ... and io.__all__.
open problems: The io.__all__ tuple contains a reference to SocketIO but SocketIO is in socket.py. from io import * fails. Should the facade SocketIO class moved to io.py or should SocketIO be removed from io.__all__? The predecessor of the patch was discussed in the sf.net bug tracker http://bugs.python.org/issue1771364 Christian
Index: Lib/socket.py =================================================================== --- Lib/socket.py (revision 57462) +++ 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._check_closed() + self._check_readable() return self._sock.recv_into(b) def write(self, b): + self._check_closed() + self._check_writable() 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 57462) +++ Lib/io.py (working copy) @@ -14,11 +14,11 @@ 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 <[EMAIL PROTECTED]>, " @@ -38,7 +38,7 @@ import io 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 @@ -105,11 +105,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) @@ -140,9 +143,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): @@ -162,9 +166,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 @@ -273,6 +278,13 @@ """ return False + def _check_seekable(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. @@ -280,6 +292,13 @@ """ return False + def _check_readable(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. @@ -287,6 +306,13 @@ """ return False + def _check_writable(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. @@ -295,6 +321,13 @@ """ return self.__closed + def _check_closed(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 @@ -321,8 +354,7 @@ Returns False if we don't know. """ - if self.closed: - raise ValueError("isatty() on closed file") + self._check_closed() return False ### Readline[s] and writelines ### @@ -354,8 +386,7 @@ return res def __iter__(self): - if self.closed: - raise ValueError("__iter__ on closed file") + self._check_closed() return self def __next__(self): @@ -377,8 +408,7 @@ return lines def writelines(self, lines): - if self.closed: - raise ValueError("write to closed file") + self._check_closed() for line in lines: self.write(line) @@ -677,7 +707,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._check_readable() _BufferedIOMixin.__init__(self, raw) self._read_buf = b"" self.buffer_size = buffer_size @@ -760,7 +790,7 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): - assert raw.writable() + raw._check_writable() _BufferedIOMixin.__init__(self, raw) self.buffer_size = buffer_size self.max_buffer_size = (2*buffer_size @@ -842,8 +872,8 @@ The arguments are two RawIO instances. """ - assert reader.readable() - assert writer.writable() + reader._check_readable() + writer._check_writable() self.reader = BufferedReader(reader, buffer_size) self.writer = BufferedWriter(writer, buffer_size, max_buffer_size) @@ -891,7 +921,7 @@ def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE, max_buffer_size=None): - assert raw.seekable() + raw._check_seekable() BufferedReader.__init__(self, raw, buffer_size) BufferedWriter.__init__(self, raw, buffer_size, max_buffer_size) @@ -1086,7 +1116,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) @@ -1122,7 +1153,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 57462) +++ Lib/test/test_io.py (working copy) @@ -738,11 +738,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() Index: Lib/test/test_sys.py =================================================================== --- Lib/test/test_sys.py (revision 57462) +++ Lib/test/test_sys.py (working copy) @@ -338,6 +338,13 @@ setattr(s, s, s) self.assertEqual(getattr(s, s), s) + def test_std_names(self): + #self.assertEqual(sys.stdin.name, '<stdin>') + #self.assertEqual(sys.stdout.name, '<stdout>') + #self.assertEqual(sys.stderr.name, '<stderr>') + self.assertEqual(sys.stdin.name, 0) + self.assertEqual(sys.stdout.name, 1) + self.assertEqual(sys.stderr.name, 2) def test_main(): test.test_support.run_unittest(SysModuleTest)
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Python-3000 mailing list Python-3000@python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com