(original) (raw)

diff -r 07d07111cec9 Lib/_pyio.py --- a/Lib/_pyio.py Wed Jan 02 18:22:23 2013 +0200 +++ b/Lib/_pyio.py Sun Jan 06 13:44:44 2013 +0000 @@ -149,6 +149,13 @@ opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode. """ + return _open(file=file, mode=mode, buffering=buffering, encoding=encoding, + errors=errors, newline=newline, closefd=closefd, + opener=opener, FileIO=FileIO, fstat=os.fstat) + + +def _open(file, mode, buffering, encoding, + errors, newline, closefd, opener, FileIO, fstat): if not isinstance(file, (str, bytes, int)): raise TypeError("invalid file: %r" % file) if not isinstance(mode, str): @@ -199,7 +206,7 @@ if buffering < 0: buffering = DEFAULT_BUFFER_SIZE try: - bs = os.fstat(raw.fileno()).st_blksize + bs = fstat(raw.fileno()).st_blksize except (OSError, AttributeError): pass else: 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 Sun Jan 06 13:44:44 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: @@ -2711,7 +2711,7 @@ for name in self.io.__all__: obj = getattr(self.io, name, None) self.assertTrue(obj is not None, name) - if name == "open": + if name in ("open", "openhandle", "closehandle"): continue elif "error" in name.lower() or name == "UnsupportedOperation": self.assertTrue(issubclass(obj, Exception), name) @@ -3183,6 +3183,32 @@ test_reentrant_write_text = None +if sys.platform == 'win32': + import winio + 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, @@ -3206,8 +3232,23 @@ 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"] = staticmethod(winio.openhandle) + win_io_ns["os_close"] = winio.closehandle + tests = list(tests) + wintests + for test in tests: if test.__name__.startswith("C"): for name, obj in c_io_ns.items(): @@ -3215,6 +3256,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 Sun Jan 06 13:44:44 2013 +0000 @@ -637,6 +637,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 Sun Jan 06 13:44:44 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 Sun Jan 06 13:44:44 2013 +0000 @@ -0,0 +1,552 @@ +#include "Python.h" +#include "pythread.h" +#include "_iomodule.h" + +#define WINDOWS_LEAN_AND_MEAN +#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 int +winio_init(winio *self, PyObject *args, PyObject *kwds) +{ + HANDLE handle; + char *mode = "r", *p, ch; + int closefd = TRUE; + BOOL ret; + static char *kwlist[] = {"handle", "mode", "closefd", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|si", kwlist, + HandleConverter, &handle, + &mode, &closefd)) + return -1; + + if (handle == INVALID_HANDLE_VALUE || handle == NULL) { + PyErr_SetString(PyExc_ValueError, "invalid handle value"); + 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 + if (!ret) { + PyErr_SetFromWindowsErr(0); + return -1; + } + } + + self->handle = INVALID_HANDLE_VALUE; + self->readable = self->writable = self->append = self->created = + self->closefd = 0; + self->seekable = self->isatty = -1; + + p = mode; + while ((ch = *p++)) { + switch (ch) { + case 'r': + self->readable = 1; + break; + case 'w': + case 'a': + self->writable = 1; + break; + case 'x': + self->writable = self->created = 1; + break; + case '+': + self->writable = self->readable = 1; + break; + case 'b': + break; + default: + PyErr_Format(PyExc_ValueError, "invalid mode: '%s'; ", mode); + return -1; + } + } + + if (closefd) + self->closefd = 1; + self->handle = handle; + 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; + + CHECK_READABLE(self); + + if (!PyArg_ParseTuple(args, "|n", &size)) + return NULL; + + if (size < 0) { + _Py_IDENTIFIER(readall); + return _PyObject_CallMethodId((PyObject*)self, &PyId_readall, ""); + } + size = CLIP_SSIZE_T(size); + + buf = PyBytes_FromStringAndSize(NULL, size); + if (buf == NULL || size == 0) + return buf; + + Py_BEGIN_ALLOW_THREADS + success = ReadFile(self->handle, PyBytes_AS_STRING(buf), + 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; + + 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; + + size = CLIP_SSIZE_T(buf.len); + + Py_BEGIN_ALLOW_THREADS + if (size > 0x7fff && _winio_isatty(self)) + 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, &oldsize, 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) +{ + return PyUnicode_FromFormat( + "%s(handle=%zu, mode='%s')", + Py_TYPE(self)->tp_name, self->handle, mode_string(self)); +} + +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_fileno(winio *self) +{ + CHECK_NOT_CLOSED(self); + return PyLong_FromSize_t((size_t)self->handle); +} + +static PyObject * +winio_isatty(winio *self) +{ + CHECK_NOT_CLOSED(self); + return PyBool_FromLong(_winio_isatty(self)); +} + +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, void *closure) +{ + 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}, + {"fileno", (PyCFunction)winio_fileno, METH_NOARGS }, + {"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 }, + {NULL} +}; + +static PyGetSetDef winio_getsets[] = { + {"mode", (getter)winio_getmode }, + {"closed", (getter)winio_getclosed }, + {"closefd", (getter)winio_getclosefd }, + {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 Sun Jan 06 13:44:44 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 Sun Jan 06 13:44:44 2013 +0000 @@ -543,6 +543,9 @@ Modules\_io + + Modules\_io+ Modules\_io