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)

Attachment: 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

Reply via email to