diff -r 3dc2a113e8a7 Doc/library/os.rst --- a/Doc/library/os.rst Tue Jun 30 22:13:56 2015 -0400 +++ b/Doc/library/os.rst Tue Jun 30 23:15:39 2015 -0400 @@ -968,8 +968,8 @@ .. function:: pipe() - Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for - reading and writing, respectively. The new file descriptor is + Create a pipe. Return a pair of file descriptors ``(readfd, writefd)`` + usable for reading and writing, respectively. The new file descriptor is :ref:`non-inheritable `. Availability: Unix, Windows. @@ -977,19 +977,27 @@ .. versionchanged:: 3.4 The new file descriptors are now non-inheritable. + .. versionchanged:: 3.6 + The function returns a tuple-like object, whose attribute can be + accessed by indexes or names. + .. function:: pipe2(flags) Create a pipe with *flags* set atomically. *flags* can be constructed by ORing together one or more of these values: :data:`O_NONBLOCK`, :data:`O_CLOEXEC`. - Return a pair of file descriptors ``(r, w)`` usable for reading and writing, - respectively. + Return a pair of file descriptors ``(readfd, writefd)`` usable for reading + and writing, respectively. Availability: some flavors of Unix. .. versionadded:: 3.3 + .. versionchanged:: 3.6 + The function returns a tuple-like object, whose attribute can be + accessed by indexes or names. + .. function:: posix_fallocate(fd, offset, len) diff -r 3dc2a113e8a7 Lib/test/test_posix.py --- a/Lib/test/test_posix.py Tue Jun 30 22:13:56 2015 -0400 +++ b/Lib/test/test_posix.py Tue Jun 30 23:15:39 2015 -0400 @@ -617,6 +617,13 @@ os.close(reader) os.close(writer) + st = posix.pipe() + self.assertIs(st[0], st.readfd) + self.assertIs(st[1], st.writefd) + self.assertEqual(len(st), 2) + os.close(st.readfd) + os.close(st.writefd) + @unittest.skipUnless(hasattr(os, 'pipe2'), "test needs os.pipe2()") @support.requires_linux_version(2, 6, 27) def test_pipe2(self): @@ -627,6 +634,12 @@ r, w = os.pipe2(0) os.close(r) os.close(w) + st = os.pipe2(0) + self.assertIs(st[0], st.readfd) + self.assertIs(st[1], st.writefd) + self.assertEqual(len(st), 2) + os.close(st.readfd) + os.close(st.writefd) # test flags r, w = os.pipe2(os.O_CLOEXEC|os.O_NONBLOCK) diff -r 3dc2a113e8a7 Modules/posixmodule.c --- a/Modules/posixmodule.c Tue Jun 30 22:13:56 2015 -0400 +++ b/Modules/posixmodule.c Tue Jun 30 23:15:39 2015 -0400 @@ -8450,6 +8450,46 @@ } +#if defined(HAVE_PIPE) || defined(HAVE_PIPE2) +static PyStructSequence_Field struct_os_pipe_fields[] = { + {"readfd", "fd for reading"}, + {"writefd", "fd for writing"}, + {0} +}; + +PyDoc_STRVAR(struct_os_pipe__doc__, +"Returned from os.pipe() and os.pipe2(flags) functions.\n\n\ +This object may be accessed either as a tuple of\n\ +(readfd, writefd) or via attributes."); + +static PyStructSequence_Desc struct_os_pipe_desc = { + "os.struct_pipe", + struct_os_pipe__doc__, + struct_os_pipe_fields, + 2, +}; + +static PyTypeObject StructPipeType; + +static PyObject * +_make_pipe_struct(int r, int w) +{ + PyObject *st = PyStructSequence_New(&StructPipeType); + if (st == NULL) + return NULL; + + PyStructSequence_SET_ITEM(st, 0, PyLong_FromLong((long) r)); + PyStructSequence_SET_ITEM(st, 1, PyLong_FromLong((long) w)); + if (PyErr_Occurred()) { + Py_DECREF(st); + return NULL; + } + + return st; +} +#endif + + #ifdef HAVE_PIPE /*[clinic input] os.pipe @@ -8526,7 +8566,7 @@ if (res != 0) return PyErr_SetFromErrno(PyExc_OSError); #endif /* !MS_WINDOWS */ - return Py_BuildValue("(ii)", fds[0], fds[1]); + return _make_pipe_struct(fds[0], fds[1]); } #endif /* HAVE_PIPE */ @@ -8557,7 +8597,7 @@ res = pipe2(fds, flags); if (res != 0) return posix_error(); - return Py_BuildValue("(ii)", fds[0], fds[1]); + return _make_pipe_struct(fds[0], fds[1]); } #endif /* HAVE_PIPE2 */ @@ -13131,6 +13171,14 @@ } PyModule_AddObject(m, "_have_functions", list); +#if defined(HAVE_PIPE) || defined(HAVE_PIPE2) + if (!initialized) { + if (PyStructSequence_InitType2(&StructPipeType, + &struct_os_pipe_desc) < 0) + return NULL; + } +#endif + initialized = 1; return m;