diff -r 07d07111cec9 Lib/_pyio.py --- a/Lib/_pyio.py Wed Jan 02 18:22:23 2013 +0200 +++ b/Lib/_pyio.py Tue Jan 15 12:24:19 2013 +0000 @@ -32,7 +32,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, - newline=None, closefd=True, opener=None): + newline=None, closefd=True, opener=None, *, rawfiletype=None): r"""Open file and return a stream. Raise OSError upon failure. @@ -185,26 +185,29 @@ raise ValueError("binary mode doesn't take an errors argument") if binary and newline is not None: raise ValueError("binary mode doesn't take a newline argument") - raw = FileIO(file, - (creating and "x" or "") + - (reading and "r" or "") + - (writing and "w" or "") + - (appending and "a" or "") + - (updating and "+" or ""), - closefd, opener=opener) + if rawfiletype is None: + rawfiletype = FileIO + raw = rawfiletype(file, + (creating and "x" or "") + + (reading and "r" or "") + + (writing and "w" or "") + + (appending and "a" or "") + + (updating and "+" or ""), + closefd, opener=opener) line_buffering = False if buffering == 1 or buffering < 0 and raw.isatty(): buffering = -1 line_buffering = True if buffering < 0: buffering = DEFAULT_BUFFER_SIZE - try: - bs = os.fstat(raw.fileno()).st_blksize - except (OSError, AttributeError): - pass - else: - if bs > 1: - buffering = bs + if rawfiletype is FileIO: + try: + bs = os.fstat(raw.fileno()).st_blksize + except (OSError, AttributeError): + pass + else: + if bs > 1: + buffering = bs if buffering < 0: raise ValueError("invalid buffering size") if buffering == 0: diff -r 07d07111cec9 Lib/io.py --- a/Lib/io.py Wed Jan 02 18:22:23 2013 +0200 +++ b/Lib/io.py Tue Jan 15 12:24:19 2013 +0000 @@ -90,3 +90,10 @@ for klass in (StringIO, TextIOWrapper): TextIOBase.register(klass) del klass + +import sys +if sys.platform == 'win32': + __all__.append('WinFileIO') + WinFileIO = _io.WinFileIO + RawIOBase.register(WinFileIO) +del sys diff -r 07d07111cec9 Lib/test/test_io.py --- a/Lib/test/test_io.py Wed Jan 02 18:22:23 2013 +0200 +++ b/Lib/test/test_io.py Tue Jan 15 12:24:19 2013 +0000 @@ -639,7 +639,7 @@ def test_opener(self): with self.open(support.TESTFN, "w") as f: f.write("egg\n") - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = self.os_open(support.TESTFN, os.O_RDONLY) def opener(path, flags): return fd with self.open("non-existent", "r", opener=opener) as f: @@ -2709,6 +2709,8 @@ def test___all__(self): for name in self.io.__all__: + if name == 'WinFileIO': + continue obj = getattr(self.io, name, None) self.assertTrue(obj is not None, name) if name == "open": @@ -3183,6 +3185,55 @@ test_reentrant_write_text = None +if sys.platform == 'win32': + # create fake version of the io module which uses windows handles + winio = type(sys)('winio') + + def winopen(*args, **kwds): + if kwds.get('rawfiletype') is None: + kwds['rawfiletype'] = WinFileIOWithFileno + return io.open(*args, **kwds) + + class WinFileIOWithFileno(io.WinFileIO): + # WinFileIO does not have a fileno() method + def fileno(self): + return self.handle + + for name in io.__all__: + obj = getattr(io, name) + setattr(winio, name, obj) + + winio.IncrementalNewlineDecoder = io.IncrementalNewlineDecoder + winio.FileIO = WinFileIOWithFileno + winio.open = winopen + winio.__all__ = io.__all__ + + class WinIOTest(CIOTest): + pass + class WinBufferedReaderTest(CBufferedReaderTest): + pass + class WinBufferedWriterTest(CBufferedWriterTest): + pass + class WinBufferedRWPairTest(CBufferedRWPairTest): + pass + class WinBufferedRandomTest(CBufferedRandomTest): + pass + class WinIncrementalNewlineDecoderTest(CIncrementalNewlineDecoderTest): + pass + class WinTextIOWrapperTest(CTextIOWrapperTest): + pass + class WinMiscIOTest(CMiscIOTest): + io = winio + + wintests = [WinIOTest, WinBufferedReaderTest, WinBufferedWriterTest, + WinBufferedRWPairTest, WinBufferedRandomTest, + WinIncrementalNewlineDecoderTest, WinTextIOWrapperTest, + WinMiscIOTest] + +else: + wintests = [] + + def test_main(): tests = (CIOTest, PyIOTest, CBufferedReaderTest, PyBufferedReaderTest, @@ -3201,13 +3252,32 @@ mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead) all_members = io.__all__ + ["IncrementalNewlineDecoder"] + try: + all_members.remove('WinFileIO') + except ValueError: + pass c_io_ns = {name : getattr(io, name) for name in all_members} py_io_ns = {name : getattr(pyio, name) for name in all_members} globs = globals() c_io_ns.update((x.__name__, globs["C" + x.__name__]) for x in mocks) py_io_ns.update((x.__name__, globs["Py" + x.__name__]) for x in mocks) - # Avoid turning open into a bound method. + + c_io_ns["os_open"] = os.open + c_io_ns["os_close"] = os.close + py_io_ns["open"] = pyio.OpenWrapper + py_io_ns["os_open"] = os.open + py_io_ns["os_close"] = os.close + + if wintests: + win_io_ns = {name : getattr(winio, name) for name in all_members} + win_io_ns.update((x.__name__, globs["C" + x.__name__]) for x in mocks) + win_io_ns["open"] = staticmethod(winio.open) + win_io_ns["FileIO"] = winio.FileIO + win_io_ns["os_open"] = winio.FileIO.openhandle + win_io_ns["os_close"] = winio.FileIO.closehandle + tests = list(tests) + wintests + for test in tests: if test.__name__.startswith("C"): for name, obj in c_io_ns.items(): @@ -3215,6 +3285,9 @@ elif test.__name__.startswith("Py"): for name, obj in py_io_ns.items(): setattr(test, name, obj) + elif test.__name__.startswith("Win"): + for name, obj in win_io_ns.items(): + setattr(test, name, obj) support.run_unittest(*tests) diff -r 07d07111cec9 Modules/_io/_iomodule.c --- a/Modules/_io/_iomodule.c Wed Jan 02 18:22:23 2013 +0200 +++ b/Modules/_io/_iomodule.c Tue Jan 15 12:24:19 2013 +0000 @@ -219,8 +219,8 @@ { char *kwlist[] = {"file", "mode", "buffering", "encoding", "errors", "newline", - "closefd", "opener", NULL}; - PyObject *file, *opener = Py_None; + "closefd", "opener", "rawfiletype", NULL}; + PyObject *file, *opener = Py_None, *rawfiletype = Py_None; char *mode = "r"; int buffering = -1, closefd = 1; char *encoding = NULL, *errors = NULL, *newline = NULL; @@ -238,10 +238,10 @@ _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO$O:open", kwlist, &file, &mode, &buffering, &encoding, &errors, &newline, - &closefd, &opener)) { + &closefd, &opener, &rawfiletype)) { return NULL; } @@ -344,7 +344,9 @@ } /* Create the Raw file stream */ - raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type, + if (rawfiletype == Py_None) + rawfiletype = (PyObject *)&PyFileIO_Type; + raw = PyObject_CallFunction(rawfiletype, "OsiO", file, rawmode, closefd, opener); if (raw == NULL) return NULL; @@ -374,7 +376,7 @@ if (buffering < 0) { buffering = DEFAULT_BUFFER_SIZE; #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - { + if (rawfiletype == (PyObject *)&PyFileIO_Type) { struct stat st; long fileno; PyObject *res = _PyObject_CallMethodId(raw, &PyId_fileno, NULL); @@ -637,6 +639,12 @@ PyFileIO_Type.tp_base = &PyRawIOBase_Type; ADD_TYPE(&PyFileIO_Type, "FileIO"); +#ifdef MS_WINDOWS + /* WinFileIO */ + PyWinFileIO_Type.tp_base = &PyRawIOBase_Type; + ADD_TYPE(&PyWinFileIO_Type, "WinFileIO"); +#endif + /* BytesIO */ PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type; ADD_TYPE(&PyBytesIO_Type, "BytesIO"); diff -r 07d07111cec9 Modules/_io/_iomodule.h --- a/Modules/_io/_iomodule.h Wed Jan 02 18:22:23 2013 +0200 +++ b/Modules/_io/_iomodule.h Tue Jan 15 12:24:19 2013 +0000 @@ -10,6 +10,9 @@ /* Concrete classes */ extern PyTypeObject PyFileIO_Type; +#ifdef MS_WINDOWS +extern PyTypeObject PyWinFileIO_Type; +#endif extern PyTypeObject PyBytesIO_Type; extern PyTypeObject PyStringIO_Type; extern PyTypeObject PyBufferedReader_Type; diff -r 07d07111cec9 Modules/_io/winfileio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_io/winfileio.c Tue Jan 15 12:24:19 2013 +0000 @@ -0,0 +1,801 @@ +#include "Python.h" +#include "pythread.h" +#include "_iomodule.h" + +#define WINDOWS_LEAN_AND_MEAN +#include +#include + +/* + * Convert a Python integer to a HANDLE (for use with PyArg_ParseTuple()) + */ + +static int +HandleConverter(PyObject *obj, HANDLE *h) +{ + size_t n = PyLong_AsSize_t(obj); + if (n == (size_t)-1 && PyErr_Occurred()) + return 0; + *h = (HANDLE)n; + return 1; +} + +#define CHECK_NOT_CLOSED(f) \ + if ((f)->handle == INVALID_HANDLE_VALUE) { \ + PyErr_SetString(PyExc_ValueError, "file closed"); \ + return NULL; \ + } + +#define CHECK_READABLE(f) \ + CHECK_NOT_CLOSED(f) \ + if (!(f)->readable) { \ + PyErr_SetString(IO_STATE->unsupported_operation, \ + "file not readable"); \ + return NULL; \ + } + +#define CHECK_WRITABLE(f) \ + CHECK_NOT_CLOSED(f) \ + if (!(f)->writable) { \ + PyErr_SetString(IO_STATE->unsupported_operation, \ + "file not writable"); \ + return NULL; \ + } + +#if SIZEOF_SIZE_T > SIZEOF_INT +# define CLIP_SSIZE_T(n) Py_MIN((n), (Py_ssize_t)UINT_MAX) +#else +# define CLIP_SSIZE_T(n) (n) +#endif + +typedef struct _winio { + /* inhetited from RawIOBase */ + PyObject_HEAD + PyObject *dict; + PyObject *weakreflist; + /* new memebers */ + HANDLE handle; + unsigned readable : 1; + unsigned writable : 1; + unsigned append : 1; + unsigned created : 1; + unsigned closefd : 1; + signed int seekable : 2; /* -1 means unknown */ + signed int isatty : 2; /* -1 means unknown */ +} winio; + +static int +_winio_isatty(winio *self) +{ + if (self->isatty < 0) + self->isatty = (GetFileType(self->handle) == FILE_TYPE_CHAR); + return self->isatty; +} + +static BOOL +_winio_openflags(DWORD flags, DWORD mode, DWORD *share_flags, + DWORD *access_flags, DWORD *create_flags, DWORD *attrib_flags) +{ + /* Note that share_flags must be initalized */ + + /* O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2 */ + switch (flags & (3 | O_APPEND)) { + case 0: + *access_flags = GENERIC_READ; + break; + case O_WRONLY: + *access_flags = GENERIC_WRITE; + break; + case O_RDWR: + *access_flags = GENERIC_READ | GENERIC_WRITE; + break; + case 0 + O_APPEND: + case O_WRONLY | O_APPEND: + *access_flags = FILE_APPEND_DATA; + break; + case O_RDWR | O_APPEND: + *access_flags = GENERIC_READ | FILE_APPEND_DATA; + break; + default: + PyErr_SetString(PyExc_ValueError, "bad flags"); + return FALSE; + } + + switch (flags & (O_CREAT | O_EXCL | O_TRUNC)) { + case 0: + case O_EXCL: + *create_flags = OPEN_EXISTING; + break; + case O_CREAT: + *create_flags = OPEN_ALWAYS; + break; + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + *create_flags = CREATE_NEW; + break; + case O_TRUNC: + case O_TRUNC | O_EXCL: + *create_flags = TRUNCATE_EXISTING; + break; + case O_CREAT | O_TRUNC: + *create_flags = CREATE_ALWAYS; + break; + default: + PyErr_SetString(PyExc_RuntimeError, "should not get here"); + return FALSE; + } + + if (flags & O_CREAT && !(mode & _S_IWRITE)) + *attrib_flags = FILE_ATTRIBUTE_READONLY; + else + *attrib_flags = FILE_ATTRIBUTE_NORMAL; + + if (flags & O_TEMPORARY) { + *share_flags |= FILE_SHARE_DELETE; + *attrib_flags |= FILE_FLAG_DELETE_ON_CLOSE; + *access_flags |= DELETE; + } + + if (flags & _O_SHORT_LIVED) + *attrib_flags |= FILE_ATTRIBUTE_TEMPORARY; + + if (flags & O_SEQUENTIAL) + *attrib_flags |= FILE_FLAG_SEQUENTIAL_SCAN; + else if (flags & O_RANDOM) + *attrib_flags |= FILE_FLAG_RANDOM_ACCESS; + + return TRUE; +} + +PyObject* +winio_openhandle(PyObject *_, PyObject *args, PyObject *kwds) +{ + HANDLE h = INVALID_HANDLE_VALUE; + PyObject *filename_obj; + DWORD flags; + DWORD mode = 0600; + DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD access, create, attrib; + static char *kwlist[] = {"filename", "flags", "mode", "share", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OI|I$I", kwlist, + &filename_obj, &flags, &mode, &share)) + return NULL; + + if (mode > 0777) { + PyErr_Format(PyExc_ValueError, "bad mode: %u", mode); + return NULL; + } + + if (!_winio_openflags(flags, mode, &share, &access, &create, &attrib)) + return NULL; + + if (PyUnicode_Check(filename_obj)) { + wchar_t *filename; + int ret = _PyUnicode_HasNULChars(filename_obj); + if (ret == 1) + PyErr_SetString(PyExc_TypeError, "embedded NUL character"); + if (ret != 0) + return NULL; + filename = PyUnicode_AsWideCharString(filename_obj, NULL); + if (filename == NULL) + return NULL; + Py_BEGIN_ALLOW_THREADS + h = CreateFileW(filename, access, share, NULL, create, attrib, NULL); + Py_END_ALLOW_THREADS + PyMem_Free(filename); + } + else if (PyBytes_Check(filename_obj)) { + char *filename = PyBytes_AS_STRING(filename_obj); + if (strlen(filename) != PyBytes_Size(filename_obj)) { + PyErr_SetString(PyExc_TypeError, "embedded NUL character"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + h = CreateFileA(filename, access, share, NULL, create, attrib, NULL); + Py_END_ALLOW_THREADS + } + else { + PyErr_SetString(PyExc_TypeError, "filename must be str or bytes"); + return NULL; + } + + if (h == INVALID_HANDLE_VALUE) + return PyErr_SetFromWindowsErr(0); + + return PyLong_FromSize_t((size_t) h); +} + +PyObject * +winio_closehandle(PyObject *self, PyObject *handle_obj) +{ + HANDLE h; + BOOL success; + + h = (HANDLE)PyLong_AsSize_t(handle_obj); + + Py_BEGIN_ALLOW_THREADS + success = CloseHandle(h); + Py_END_ALLOW_THREADS + + if (!success) + return PyErr_SetFromWindowsErr(0); + Py_RETURN_NONE; +} + +static int +winio_init(winio *self, PyObject *args, PyObject *kwds) +{ + HANDLE handle = INVALID_HANDLE_VALUE; + PyObject *name, *opener, *handle_obj; + char *mode, *p, ch; + unsigned readable, writable, created, append, plus, binary, closefd, flags; + BOOL ret; + LARGE_INTEGER zero; + static char *kwlist[] = {"name", "mode", "closefd", "opener", NULL}; + + mode = "r"; + closefd = TRUE; + opener = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sIO", kwlist, + &name, &mode, &closefd, &opener)) + return -1; + + readable = writable = append = created = binary = plus = binary = 0; + + for (p = mode; ch = *p++; ) { + switch (ch) { + case 'r': readable++; break; + case 'w': writable++; break; + case 'a': append++; break; + case 'x': created++; break; + case '+': plus++; break; + case 'b': binary++; break; + default: plus = 2; + } + } + + if (readable + writable + append + created != 1 || + plus > 1 || binary > 1) + { + PyErr_Format(PyExc_ValueError, "invalid mode: '%s'; ", mode); + return -1; + } + + if (self->closefd && self->handle != NULL && + self->handle != INVALID_HANDLE_VALUE) + { + Py_BEGIN_ALLOW_THREADS + ret = CloseHandle(self->handle); + Py_END_ALLOW_THREADS + self->handle = INVALID_HANDLE_VALUE; + if (!ret) { + DWORD err = GetLastError(); + CloseHandle(handle); + PyErr_SetFromWindowsErr(err); + return -1; + } + } + + if (PyUnicode_Check(name) || PyBytes_Check(name)) { + if (!closefd) { + PyErr_SetString(PyExc_ValueError, + "closefd must be true if name is not an int"); + return -1; + } + if (plus) { + flags = O_RDWR; + } else if (readable) { + flags = O_RDONLY; + } else { + flags = O_WRONLY; + } + if (writable) { + flags |= O_CREAT | O_TRUNC; + } else if (created) { + flags |= O_CREAT | O_EXCL; + } else if (append) { + flags |= O_CREAT | O_APPEND; + } + if (opener == Py_None) { + handle_obj = PyObject_CallMethod((PyObject *) self, "openhandle", + "OI", name, flags); + } else { + handle_obj = PyObject_CallFunction(opener, "OI", name, flags); + } + if (handle_obj == NULL) + return -1; + handle = (HANDLE)PyLong_AsSize_t(handle_obj); + Py_DECREF(handle_obj); + } + else { + handle = (HANDLE)PyLong_AsSize_t(name); + } + + if (handle == (HANDLE)-1 && PyErr_Occurred()) + return -1; + + if (handle == INVALID_HANDLE_VALUE) { + PyErr_SetString(PyExc_ValueError, "invalid handle"); + return -1; + } + + if (append) { + zero.QuadPart = 0; + Py_BEGIN_ALLOW_THREADS + ret = SetFilePointerEx(handle, zero, NULL, FILE_END); + Py_END_ALLOW_THREADS + if (!ret) { + if (!PyLong_Check(name)) { + Py_BEGIN_ALLOW_THREADS + CloseHandle(handle); + Py_END_ALLOW_THREADS + } + PyErr_SetFromWindowsErr(0); + return -1; + } + } + + self->readable = readable || plus; + self->writable = writable || append || created || plus; + self->append = append; + self->created = created; + self->closefd = closefd != 0; + self->seekable = -1; + self->isatty = -1; + self->handle = handle; + + if (PyObject_SetAttrString((PyObject *) self, "name", name) < 0) + return -1; + + return 0; +} + +static int +winio_traverse(winio *self, visitproc visit, void *arg) +{ + Py_VISIT(self->dict); + return 0; +} + +static int +winio_clear(winio *self) +{ + Py_CLEAR(self->dict); + return 0; +} + +static void +winio_dealloc(winio *self) +{ + if (self->handle != INVALID_HANDLE_VALUE && self->closefd) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (PyErr_WarnFormat(PyExc_ResourceWarning, 1, + "unclosed file %R", (PyObject *)self)) + { + /* Spurious errors can appear at shutdown */ + if (PyErr_ExceptionMatches(PyExc_Warning)) + PyErr_WriteUnraisable((PyObject *) self); + } + PyErr_Restore(exc, val, tb); + } + if (_PyIOBase_finalize((PyObject *) self) < 0) + return; + _PyObject_GC_UNTRACK(self); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); + Py_CLEAR(self->dict); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject * +winio_close(winio *self) +{ + _Py_IDENTIFIER(close); + HANDLE h = self->handle; + BOOL success; + + if (h != INVALID_HANDLE_VALUE && h != NULL) { + self->handle = INVALID_HANDLE_VALUE; + if (self->closefd) { + Py_BEGIN_ALLOW_THREADS + success = CloseHandle(h); + Py_END_ALLOW_THREADS + if (!success) + return PyErr_SetFromWindowsErr(0); + } + } + return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type, + &PyId_close, "O", self); +} + +static PyObject * +winio_read(winio *self, PyObject *args) +{ + BOOL success; + Py_ssize_t size = -1; + DWORD nbytes; + PyObject *buf; + char *ptr; + + CHECK_READABLE(self); + + if (!PyArg_ParseTuple(args, "|n", &size)) + return NULL; + + if (size < 0) { + _Py_IDENTIFIER(readall); + return _PyObject_CallMethodId((PyObject*)self, &PyId_readall, ""); + } + + /* ensure size <= DWORD_MAX since ReadFile() uses DWORD size paramter */ + size = CLIP_SSIZE_T(size); + + buf = PyBytes_FromStringAndSize(NULL, size); + if (buf == NULL || size == 0) + return buf; + + ptr = PyBytes_AS_STRING(buf); + + Py_BEGIN_ALLOW_THREADS + success = ReadFile(self->handle, ptr, size, &nbytes, FALSE); + Py_END_ALLOW_THREADS + + switch (success ? ERROR_SUCCESS : GetLastError()) { + case ERROR_BROKEN_PIPE: + nbytes = 0; + /* fall through */ + case ERROR_SUCCESS: + if (nbytes == size || _PyBytes_Resize(&buf, nbytes) >= 0) + return buf; + break; + default: + PyErr_SetFromWindowsErr(0); + } + + Py_DECREF(buf); + return NULL; +} + +static PyObject * +winio_readinto(winio *self, PyObject *obj) +{ + BOOL success; + DWORD nbytes; + DWORD size; + Py_buffer buf; + + CHECK_READABLE(self); + + if (PyObject_GetBuffer(obj, &buf, PyBUF_WRITABLE) < 0) + return NULL; + + /* ensure size <= DWORD_MAX since ReadFile() uses DWORD size paramter */ + size = CLIP_SSIZE_T(buf.len); + + if (size == 0) { + PyBuffer_Release(&buf); + return PyLong_FromLong(0); + } + + Py_BEGIN_ALLOW_THREADS + success = ReadFile(self->handle, buf.buf, size, &nbytes, NULL); + Py_END_ALLOW_THREADS + + PyBuffer_Release(&buf); + + switch (success ? ERROR_SUCCESS : GetLastError()) { + case ERROR_SUCCESS: + return PyLong_FromUnsignedLong(nbytes); + case ERROR_BROKEN_PIPE: + return PyLong_FromLong(0); + default: + return PyErr_SetFromWindowsErr(0); + } +} + +static PyObject * +winio_write(winio *self, PyObject *obj) +{ + DWORD size; + DWORD nbytes; + BOOL success; + Py_buffer buf; + + CHECK_WRITABLE(self); + + if (PyObject_GetBuffer(obj, &buf, PyBUF_SIMPLE) < 0) + return NULL; + + /* ensure size <= DWORD_MAX since WriteFile() uses DWORD size paramter */ + size = CLIP_SSIZE_T(buf.len); + + Py_BEGIN_ALLOW_THREADS + if (size > 0x7fff && _winio_isatty(self)) { + /* 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). */ + size = 0x7fff; + } + success = WriteFile(self->handle, buf.buf, size, &nbytes, NULL); + Py_END_ALLOW_THREADS + + PyBuffer_Release(&buf); + + if (!success) + return PyErr_SetFromWindowsErr(0); + + return PyLong_FromUnsignedLong(nbytes); +} + +static PyObject * +winio_tell(winio *self) +{ + LARGE_INTEGER zero, newpos; + BOOL success; + + CHECK_NOT_CLOSED(self); + zero.QuadPart = 0; + + Py_BEGIN_ALLOW_THREADS + success = SetFilePointerEx(self->handle, zero, &newpos, FILE_CURRENT); + Py_END_ALLOW_THREADS + + if (!success) + return PyErr_SetFromWindowsErr(0); + + return PyLong_FromLongLong(newpos.QuadPart); +} + +static PyObject * +winio_seek(winio *self, PyObject *args) +{ + LARGE_INTEGER offset, newpos; + DWORD whence = FILE_BEGIN; + BOOL success; + + CHECK_NOT_CLOSED(self); + + if (!PyArg_ParseTuple(args, "L|k", &offset.QuadPart, &whence)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + success = SetFilePointerEx(self->handle, offset, &newpos, whence); + Py_END_ALLOW_THREADS + + if (!success) + return PyErr_SetFromWindowsErr(0); + + return PyLong_FromLongLong(newpos.QuadPart); +} + +static PyObject * +winio_truncate(winio *self, PyObject *args) +{ + PyObject *size_obj = Py_None; + LARGE_INTEGER zero, size, oldsize; + BOOL ret; + + CHECK_WRITABLE(self); + zero.QuadPart = 0; + + if (!PyArg_ParseTuple(args, "|O", &size_obj)) + return NULL; + + if (size_obj == Py_None) { + Py_BEGIN_ALLOW_THREADS + ret = (SetEndOfFile(self->handle) && + SetFilePointerEx(self->handle, zero, &size, FILE_CURRENT)); + Py_END_ALLOW_THREADS + } else { + size.QuadPart = PyLong_AsLongLong(size_obj); + if (size.QuadPart == -1 && PyErr_Occurred()) + return NULL; + if (size.QuadPart < 0) { + PyErr_SetString(PyExc_ValueError, "negative size"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + ret = (SetFilePointerEx(self->handle, zero, &oldsize, FILE_CURRENT) && + SetFilePointerEx(self->handle, size, NULL, FILE_BEGIN) && + SetEndOfFile(self->handle) && + SetFilePointerEx(self->handle, oldsize, NULL, FILE_BEGIN)); + Py_END_ALLOW_THREADS + } + + if (!ret) + return PyErr_SetFromWindowsErr(0); + + return PyLong_FromLongLong(size.QuadPart); +} + +static char * +mode_string(winio *self) +{ + if (self->created) { + if (self->readable) + return "xb+"; + else + return "xb"; + } + if (self->readable) { + if (self->writable) + return "rb+"; + else + return "rb"; + } + else + return "wb"; +} + +static PyObject * +winio_repr(winio *self) +{ + _Py_IDENTIFIER(name); + PyObject *nameobj, *res; + + if (self->handle == INVALID_HANDLE_VALUE) + return PyUnicode_FromFormat("<_io.WinFileIO [closed]>"); + + nameobj = _PyObject_GetAttrId((PyObject *) self, &PyId_name); + if (nameobj == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + res = PyUnicode_FromFormat("<_io.WinFileIO handle=%zu mode='%s'>", + self->handle, mode_string(self)); + } + else { + res = PyUnicode_FromFormat("<_io.WinFileIO name=%R mode='%s'>", + nameobj, mode_string(self)); + Py_DECREF(nameobj); + } + return res; +} + +static PyObject * +winio_readable(winio *self) +{ + return PyBool_FromLong(self->readable); +} + +static PyObject * +winio_writable(winio *self) +{ + return PyBool_FromLong(self->writable); +} + +static PyObject * +winio_seekable(winio *self) +{ + LARGE_INTEGER zero; + BOOL success; + + CHECK_NOT_CLOSED(self); + + if (self->seekable < 0) { + zero.QuadPart = 0; + Py_BEGIN_ALLOW_THREADS + success = SetFilePointerEx(self->handle, zero, NULL, FILE_CURRENT); + Py_END_ALLOW_THREADS + self->seekable = success != 0; + } + + return PyBool_FromLong(self->seekable); +} + +static PyObject * +winio_isatty(winio *self) +{ + CHECK_NOT_CLOSED(self); + return PyBool_FromLong(_winio_isatty(self)); +} + +static PyObject * +winio_gethandle(winio *self) +{ + CHECK_NOT_CLOSED(self); + return PyLong_FromSize_t((size_t)self->handle); +} + +static PyObject * +winio_getstate(winio *self) +{ + PyErr_Format(PyExc_TypeError, + "cannot serialize '%s' object", Py_TYPE(self)->tp_name); + return NULL; +} + +static PyObject * +winio_getmode(winio *self) +{ + return PyUnicode_InternFromString(mode_string(self)); +} + +static PyObject * +winio_getclosed(winio *self) +{ + return PyBool_FromLong(self->handle == INVALID_HANDLE_VALUE); +} + +static PyObject * +winio_getclosefd(winio *self) +{ + return PyBool_FromLong(self->closefd); +} + +static PyMethodDef winio_methods[] = { + {"read", (PyCFunction)winio_read, METH_VARARGS}, + {"readinto", (PyCFunction)winio_readinto, METH_O, }, + {"write", (PyCFunction)winio_write, METH_O, }, + {"close", (PyCFunction)winio_close, METH_NOARGS }, + {"tell", (PyCFunction)winio_tell, METH_NOARGS }, + {"seek", (PyCFunction)winio_seek, METH_VARARGS}, + {"truncate", (PyCFunction)winio_truncate, METH_VARARGS}, + {"readable", (PyCFunction)winio_readable, METH_NOARGS }, + {"writable", (PyCFunction)winio_writable, METH_NOARGS }, + {"seekable", (PyCFunction)winio_seekable, METH_NOARGS }, + {"isatty", (PyCFunction)winio_isatty, METH_NOARGS }, + {"__getstate__", (PyCFunction)winio_getstate, METH_NOARGS }, + {"openhandle", (PyCFunction)winio_openhandle, + METH_VARARGS|METH_KEYWORDS|METH_STATIC}, + {"closehandle", (PyCFunction)winio_closehandle, + METH_O|METH_STATIC}, + {NULL} +}; + +static PyGetSetDef winio_getsets[] = { + {"mode", (getter)winio_getmode }, + {"closed", (getter)winio_getclosed }, + {"closefd", (getter)winio_getclosefd }, + {"handle", (getter)winio_gethandle }, + {NULL} +}; + +PyTypeObject PyWinFileIO_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_io.WinFileIO", /* tp_name */ + sizeof(winio), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)winio_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)winio_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)winio_traverse, /* tp_traverse */ + (inquiry)winio_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + winio_methods, /* tp_methods */ + 0, /* tp_members */ + winio_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)winio_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff -r 07d07111cec9 PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj Wed Jan 02 18:22:23 2013 +0200 +++ b/PCbuild/pythoncore.vcxproj Tue Jan 15 12:24:19 2013 +0000 @@ -531,6 +531,7 @@ + diff -r 07d07111cec9 PCbuild/pythoncore.vcxproj.filters --- a/PCbuild/pythoncore.vcxproj.filters Wed Jan 02 18:22:23 2013 +0200 +++ b/PCbuild/pythoncore.vcxproj.filters Tue Jan 15 12:24:19 2013 +0000 @@ -543,6 +543,9 @@ Modules\_io + + Modules\_io + Modules\_io