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 [email protected] http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com
