Skip to content

return the new file descriptor from os.dup2 (closes bpo-32441) #5041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -735,13 +735,17 @@ as internal buffering of data.

.. function:: dup2(fd, fd2, inheritable=True)

Duplicate file descriptor *fd* to *fd2*, closing the latter first if necessary.
The file descriptor *fd2* is :ref:`inheritable <fd_inheritance>` by default,
or non-inheritable if *inheritable* is ``False``.
Duplicate file descriptor *fd* to *fd2*, closing the latter first if
necessary. Return *fd2*. The new file descriptor is :ref:`inheritable
<fd_inheritance>` by default or non-inheritable if *inheritable*
is ``False``.

.. versionchanged:: 3.4
Add the optional *inheritable* parameter.

.. versionchanged:: 3.7
Return *fd2* on success. Previously, ``None`` was always returned.


.. function:: fchmod(fd, mode)

Expand Down
16 changes: 6 additions & 10 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -3079,19 +3079,15 @@ def test_dup2(self):

# inheritable by default
fd2 = os.open(__file__, os.O_RDONLY)
try:
os.dup2(fd, fd2)
self.assertEqual(os.get_inheritable(fd2), True)
finally:
os.close(fd2)
self.addCleanup(os.close, fd2)
self.assertEqual(os.dup2(fd, fd2), fd2)
self.assertTrue(os.get_inheritable(fd2))

# force non-inheritable
fd3 = os.open(__file__, os.O_RDONLY)
try:
os.dup2(fd, fd3, inheritable=False)
self.assertEqual(os.get_inheritable(fd3), False)
finally:
os.close(fd3)
self.addCleanup(os.close, fd3)
self.assertEqual(os.dup2(fd, fd3, inheritable=False), fd3)
self.assertFalse(os.get_inheritable(fd3))

@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()")
def test_openpty(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Return the new file descriptor (i.e., the second argument) from ``os.dup2``.
Previously, ``None`` was always returned.
11 changes: 8 additions & 3 deletions Modules/clinic/posixmodule.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -3486,7 +3486,7 @@ PyDoc_STRVAR(os_dup2__doc__,
#define OS_DUP2_METHODDEF \
{"dup2", (PyCFunction)os_dup2, METH_FASTCALL|METH_KEYWORDS, os_dup2__doc__},

static PyObject *
static int
os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable);

static PyObject *
Expand All @@ -3498,12 +3498,17 @@ os_dup2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn
int fd;
int fd2;
int inheritable = 1;
int _return_value;

if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&fd, &fd2, &inheritable)) {
goto exit;
}
return_value = os_dup2_impl(module, fd, fd2, inheritable);
_return_value = os_dup2_impl(module, fd, fd2, inheritable);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyLong_FromLong((long)_return_value);

exit:
return return_value;
Expand Down Expand Up @@ -6405,4 +6410,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=b6ade5f170d5a431 input=a9049054013a1b77]*/
/*[clinic end generated code: output=6345053cd5992caf input=a9049054013a1b77]*/
43 changes: 27 additions & 16 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7770,17 +7770,17 @@ os_dup_impl(PyObject *module, int fd)


/*[clinic input]
os.dup2
os.dup2 -> int
fd: int
fd2: int
inheritable: bool=True

Duplicate file descriptor.
[clinic start generated code]*/

static PyObject *
static int
os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
/*[clinic end generated code: output=db832a2d872ccc5f input=76e96f511be0352f]*/
/*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/
{
int res;
#if defined(HAVE_DUP3) && \
Expand All @@ -7789,8 +7789,10 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
int dup3_works = -1;
#endif

if (fd < 0 || fd2 < 0)
return posix_error();
if (fd < 0 || fd2 < 0) {
posix_error();
return -1;
}

/* dup2() can fail with EINTR if the target FD is already open, because it
* then has to be closed. See os_close_impl() for why we don't handle EINTR
Expand All @@ -7802,13 +7804,16 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
res = dup2(fd, fd2);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
if (res < 0) {
posix_error();
return -1;
}
res = fd2; // msvcrt dup2 returns 0 on success.

/* Character files like console cannot be make non-inheritable */
if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
close(fd2);
return NULL;
return -1;
}

#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)
Expand All @@ -7818,8 +7823,10 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
else
res = dup2(fd, fd2);
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
if (res < 0) {
posix_error();
return -1;
}

#else

Expand All @@ -7831,8 +7838,10 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
if (res < 0) {
if (dup3_works == -1)
dup3_works = (errno != ENOSYS);
if (dup3_works)
return posix_error();
if (dup3_works) {
posix_error();
return -1;
}
}
}

Expand All @@ -7842,20 +7851,22 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)
Py_BEGIN_ALLOW_THREADS
res = dup2(fd, fd2);
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error();
if (res < 0) {
posix_error();
return -1;
}

if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) {
close(fd2);
return NULL;
return -1;
}
#ifdef HAVE_DUP3
}
#endif

#endif

Py_RETURN_NONE;
return res;
}


Expand Down