diff -r 4d4277941a45 Lib/test/test_fileio.py --- a/Lib/test/test_fileio.py Fri Apr 12 23:30:59 2013 +0300 +++ b/Lib/test/test_fileio.py Sat Apr 13 17:09:35 2013 -0400 @@ -449,6 +449,21 @@ self.assertRaises(MyException, MyFileIO, fd) os.close(fd) # should not raise OSError(EBADF) + def testWin32_crt_write(self): + """ text / binary mode shouldn't change if fdopen is used. """ + + if sys.platform != 'win32': + return + + fd = os.open(TESTFN, os.O_WRONLY | os.O_CREAT | os.O_TEXT) + f = os.fdopen(fd, 'w') + os.write(fd, b'\n') + f.close() + + with open(TESTFN, 'rb') as f: + self.assertEqual(f.read(), b'\r\n') + + assert False def test_main(): # Historically, these tests have been sloppy about removing TESTFN. diff -r 4d4277941a45 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Fri Apr 12 23:30:59 2013 +0300 +++ b/Modules/_io/fileio.c Sat Apr 13 17:09:35 2013 -0400 @@ -33,6 +33,13 @@ #define HAVE_FTRUNCATE #define WIN32_LEAN_AND_MEAN #include + +#define USE_WIN32_READWRITEFILE +#ifdef USE_WIN32_READWRITEFILE +/* Undocumented CRT api for setting errno from GetLastError() + used to match the behavior of the CRT's read and write */ +extern void _dosmaperr(unsigned long error_code); +#endif #endif #if BUFSIZ < (8*1024) @@ -202,6 +209,78 @@ return 0; } +static Py_ssize_t +internal_read(int fd, void* buffer, Py_ssize_t len) +{ + Py_ssize_t n; + +#if defined(USE_WIN32_READWRITEFILE) + HANDLE handle = (HANDLE) _get_osfhandle(fd); + DWORD bytes_read; + + if (len > INT_MAX) + len = INT_MAX; + + if (handle == INVALID_HANDLE_VALUE) + /* _get_osfhandle sets errno in invalid case */ + return 0; + if (!ReadFile(handle, buffer, len, &bytes_read, NULL)) + _dosmaperr(GetLastError()); + n = bytes_read; +#elif defined(MS_WIN64) || defined(MS_WINDOWS) + if (len > INT_MAX) + len = INT_MAX; + n = read(fd, buffer, (int)len); +#else + n = read(fd, buffer, len); +#endif + + return n; +} + +static Py_ssize_t +internal_write(int fd, void* buffer, Py_ssize_t len) +{ + Py_ssize_t n; +#if defined(USE_WIN32_READWRITEFILE) + HANDLE handle = (HANDLE) _get_osfhandle(fd); + DWORD bytes_written; + + if (handle == INVALID_HANDLE_VALUE) + /* _get_osfhandle sets errno in invalid case */ + return 0; + + if (len > 32767 && isatty(fd)) { + /* Issue #11395: the Windows console returns an error (12: not + enough space error) on writing into stdout if stdout mode is + binary and the length is greater than 66,000 bytes (or less, + depending on heap usage). */ + len = 32767; + } + else if (len > INT_MAX) + len = INT_MAX; + + if (!WriteFile(handle, buffer, len, &bytes_written, NULL)) { + _dosmaperr(GetLastError()); + } + n = bytes_written; + +#elif defined(MS_WIN64) || defined(MS_WINDOWS) + if (len > 32767 && isatty(fd)) { + /* Issue #11395: the Windows console returns an error (12: not + enough space error) on writing into stdout if stdout mode is + binary and the length is greater than 66,000 bytes (or less, + depending on heap usage). */ + len = 32767; + } + else if (len > INT_MAX) + len = INT_MAX; + n = write(fd, buffer, (int)len); +#else + n = write(fd, buffer, len); +#endif + return n; +} static int fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) @@ -398,10 +477,12 @@ if (dircheck(self, nameobj) < 0) goto error; +#ifndef USE_WIN32_READWRITEFILE #if defined(MS_WINDOWS) || defined(__CYGWIN__) /* don't translate newlines (\r\n <=> \n) */ _setmode(self->fd, O_BINARY); #endif +#endif if (PyObject_SetAttrString((PyObject *)self, "name", nameobj) < 0) goto error; @@ -533,13 +614,7 @@ len = pbuf.len; Py_BEGIN_ALLOW_THREADS errno = 0; -#if defined(MS_WIN64) || defined(MS_WINDOWS) - if (len > INT_MAX) - len = INT_MAX; - n = read(self->fd, pbuf.buf, (int)len); -#else - n = read(self->fd, pbuf.buf, len); -#endif + n = internal_read(self->fd, pbuf.buf, len); Py_END_ALLOW_THREADS } else n = -1; @@ -652,17 +727,7 @@ Py_BEGIN_ALLOW_THREADS errno = 0; n = newsize - total; -#if defined(MS_WIN64) || defined(MS_WINDOWS) - if (n > INT_MAX) - n = INT_MAX; - n = read(self->fd, - PyBytes_AS_STRING(result) + total, - (int)n); -#else - n = read(self->fd, - PyBytes_AS_STRING(result) + total, - n); -#endif + n = internal_read(self->fd, PyBytes_AS_STRING(result) + total, n); Py_END_ALLOW_THREADS if (n == 0) break; @@ -732,11 +797,7 @@ if (_PyVerify_fd(self->fd)) { Py_BEGIN_ALLOW_THREADS errno = 0; -#if defined(MS_WIN64) || defined(MS_WINDOWS) - n = read(self->fd, ptr, (int)size); -#else - n = read(self->fd, ptr, size); -#endif + n = internal_read(self->fd, ptr, size); Py_END_ALLOW_THREADS } else n = -1; @@ -780,20 +841,7 @@ Py_BEGIN_ALLOW_THREADS errno = 0; len = pbuf.len; -#if defined(MS_WIN64) || defined(MS_WINDOWS) - if (len > 32767 && isatty(self->fd)) { - /* Issue #11395: the Windows console returns an error (12: not - enough space error) on writing into stdout if stdout mode is - binary and the length is greater than 66,000 bytes (or less, - depending on heap usage). */ - len = 32767; - } - else if (len > INT_MAX) - len = INT_MAX; - n = write(self->fd, pbuf.buf, (int)len); -#else - n = write(self->fd, pbuf.buf, len); -#endif + n = internal_write(self->fd, pbuf.buf, len); Py_END_ALLOW_THREADS } else n = -1;