Author: Amaury Forgeot d'Arc <[email protected]>
Branch: py3.5
Changeset: r88369:9ffa558330fb
Date: 2016-11-13 23:22 +0100
http://bitbucket.org/pypy/pypy/changeset/9ffa558330fb/

Log:    CPython issue #21679: Prevent extraneous fstat() calls during
        open().

diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -5,9 +5,13 @@
 from rpython.rlib.rarithmetic import r_longlong
 from rpython.rlib.rstring import StringBuilder
 from rpython.rlib import rposix
+from rpython.rlib.rposix_stat import STAT_FIELD_TYPES
 from os import O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_EXCL
 import sys, os, stat, errno
-from pypy.module._io.interp_iobase import W_RawIOBase, convert_size
+from pypy.module._io.interp_iobase import (
+    W_RawIOBase, convert_size, DEFAULT_BUFFER_SIZE)
+
+HAS_BLKSIZE = 'st_blksize' in STAT_FIELD_TYPES
 
 def interp_member_w(name, cls, doc=None):
     "NOT_RPYTHON: initialization-time only"
@@ -162,12 +166,6 @@
         fd_is_own = False
         try:
             if fd >= 0:
-                try:
-                    os.fstat(fd)
-                except OSError as e:
-                    if e.errno == errno.EBADF:
-                        raise wrap_oserror(space, e)
-                    # else: pass
                 self.fd = fd
                 self.closefd = bool(closefd)
             elif space.is_none(w_opener):
@@ -207,7 +205,20 @@
                     except OSError as e:
                         raise wrap_oserror2(space, e, w_name)
 
-            self._dircheck(space, w_name)
+            try:
+                st = os.fstat(self.fd)
+            except OSError as e:
+                raise wrap_oserror(space, e)
+            # On Unix, fopen will succeed for directories.
+            # In Python, there should be no file objects referring to
+            # directories, so we need a check.
+            if stat.S_ISDIR(st.st_mode):
+                raise wrap_oserror2(space, OSError(errno.EISDIR, "fstat"),
+                                    w_name, exception_name='w_IOError')
+            self.blksize = DEFAULT_BUFFER_SIZE
+            if HAS_BLKSIZE and st.st_blksize > 1:
+                self.blksize = st.st_blksize
+
             space.setattr(self, space.wrap("name"), w_name)
 
             if self.appending:
@@ -244,6 +255,9 @@
     def descr_get_mode(self, space):
         return space.wrap(self._mode())
 
+    def get_blksize(self, space):
+        return space.wrap(self.blksize)
+
     def _closed(self, space):
         return self.fd < 0
 
@@ -298,20 +312,6 @@
                 if e.match(space, space.w_Warning):
                     e.write_unraisable(space, '', space.wrap(self))
 
-    def _dircheck(self, space, w_filename):
-        # On Unix, fopen will succeed for directories.
-        # In Python, there should be no file objects referring to
-        # directories, so we need a check.
-        if self.fd < 0:
-            return
-        try:
-            st = os.fstat(self.fd)
-        except OSError:
-            return
-        if stat.S_ISDIR(st.st_mode):
-            raise wrap_oserror2(space, OSError(errno.EISDIR, "fstat"),
-                                w_filename, exception_name='w_IOError')
-
     @unwrap_spec(pos=r_longlong, whence=int)
     def seek_w(self, space, pos, whence=0):
         self._check_closed(space)
@@ -506,5 +506,6 @@
         doc="True if the file descriptor will be closed"),
     mode = GetSetProperty(W_FileIO.descr_get_mode,
                           doc="String giving the file mode"),
+    _blksize = GetSetProperty(W_FileIO.get_blksize),
     )
 
diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py
--- a/pypy/module/_io/interp_io.py
+++ b/pypy/module/_io/interp_io.py
@@ -6,9 +6,6 @@
     TypeDef, interp_attrproperty, generic_new_descr)
 from pypy.module._io.interp_fileio import W_FileIO
 from pypy.module._io.interp_textio import W_TextIOWrapper
-from rpython.rlib.rposix_stat import STAT_FIELD_TYPES
-
-HAS_BLKSIZE = 'st_blksize' in STAT_FIELD_TYPES
 
 
 class Cache:
@@ -17,8 +14,6 @@
             "io.UnsupportedOperation",
             space.newtuple([space.w_ValueError, space.w_IOError]))
 
-DEFAULT_BUFFER_SIZE = 8 * 1024
-
 @unwrap_spec(mode=str, buffering=int,
              encoding="str_or_None", errors="str_or_None",
              newline="str_or_None", closefd=int)
@@ -96,18 +91,7 @@
         buffering = -1
 
     if buffering < 0:
-        buffering = DEFAULT_BUFFER_SIZE
-
-        if HAS_BLKSIZE:
-            fileno = space.c_int_w(space.call_method(w_raw, "fileno"))
-            try:
-                st = os.fstat(fileno)
-            except OSError:
-                # Errors should never pass silently, except this one time.
-                pass
-            else:
-                if st.st_blksize > 1:
-                    buffering = st.st_blksize
+        buffering = space.c_int_w(space.getattr(w_raw, space.wrap("_blksize")))
 
     if buffering < 0:
         raise oefmt(space.w_ValueError, "invalid buffering size")
diff --git a/pypy/module/_io/test/test_fileio.py 
b/pypy/module/_io/test/test_fileio.py
--- a/pypy/module/_io/test/test_fileio.py
+++ b/pypy/module/_io/test/test_fileio.py
@@ -21,6 +21,8 @@
         assert f.name.endswith('tmpfile')
         assert f.mode == 'ab'
         assert f.closefd is True
+        assert f._blksize >= 1024
+        assert f._blksize % 1024 == 0
         f.close()
 
     def test_invalid_fd(self):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to