Skip to content
Closed
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
2 changes: 1 addition & 1 deletion Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
Return a pair of file descriptors ``(r, w)`` usable for reading and writing,
respectively.

.. availability:: Unix, not WASI, not macOS, not iOS.
.. availability:: Unix, not WASI, macOS >= 27.0, iOS >= 27.0.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct the iOS availability claim

The documentation promises os.pipe2 on iOS 27+, but configure.ac deliberately skips the AC_CHECK_FUNCS invocation containing pipe2 whenever ac_sys_system is iOS (lines 5526–5532). Consequently HAVE_PIPE2 is never defined and the method is not compiled or exported on any iOS version; either enable the configure check for supported iOS versions or continue documenting this as unavailable on iOS.

Useful? React with 👍 / 👎.


.. versionadded:: 3.3

Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_os/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,22 @@ def test_pwritev(self):
self.assertNotHasAttr(os, "pwritev")
self.assertNotHasAttr(os, "preadv")

def test_pipe2(self):
self._verify_available("HAVE_PIPE2")
if self.mac_ver >= (27, 0):
self.assertHasAttr(os, "pipe2")
else:
self.assertNotHasAttr(os, "pipe2")

def test_dup3(self):
self._verify_available("HAVE_DUP3")
r, w = os.pipe()
self.addCleanup(os.close, r)
self.addCleanup(os.close, w)
# Must not crash even when dup3 unavailable at runtime.
# os.dup2 returns fd2 (here w); do not double-close.
os.dup2(r, w, inheritable=False)

def test_stat(self):
self._verify_available("HAVE_FSTATAT")
if self.mac_ver >= (10, 10):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Guard ``os.dup2`` and ``os.pipe``/``os.pipe2`` against calling ``dup3``/``pipe2``
when the symbols are weak-imported but unavailable at runtime (Xcode 27 SDKs on
older macOS).
57 changes: 45 additions & 12 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
# define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *)
# define HAVE_DUP3_RUNTIME __builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, *)
# define HAVE_PIPE2_RUNTIME __builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, *)

# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)

Expand Down Expand Up @@ -589,6 +591,14 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL)
# endif

# ifdef HAVE_DUP3
# define HAVE_DUP3_RUNTIME (dup3 != NULL)
# endif

# ifdef HAVE_PIPE2
# define HAVE_PIPE2_RUNTIME (pipe2 != NULL)
# endif

#endif

#ifdef HAVE_FUTIMESAT
Expand Down Expand Up @@ -619,6 +629,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
# define HAVE_MKFIFOAT_RUNTIME 1
# define HAVE_MKNODAT_RUNTIME 1
# define HAVE_PTSNAME_R_RUNTIME 1
# define HAVE_DUP3_RUNTIME 1
# define HAVE_PIPE2_RUNTIME 1
#endif


Expand Down Expand Up @@ -11910,17 +11922,22 @@ os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable)

#ifdef HAVE_DUP3
if (!inheritable && dup3_works != 0) {
Py_BEGIN_ALLOW_THREADS
res = dup3(fd, fd2, O_CLOEXEC);
Py_END_ALLOW_THREADS
if (res < 0) {
if (dup3_works == -1)
dup3_works = (errno != ENOSYS);
if (dup3_works) {
posix_error();
return -1;
if (HAVE_DUP3_RUNTIME) {
Py_BEGIN_ALLOW_THREADS
res = dup3(fd, fd2, O_CLOEXEC);
Py_END_ALLOW_THREADS
if (res < 0) {
if (dup3_works == -1)
dup3_works = (errno != ENOSYS);
if (dup3_works) {
posix_error();
return -1;
}
}
}
else {
dup3_works = 0;
}
}

if (inheritable || dup3_works == 0)
Expand Down Expand Up @@ -12779,9 +12796,13 @@ os_pipe_impl(PyObject *module)
#else

#ifdef HAVE_PIPE2
Py_BEGIN_ALLOW_THREADS
res = pipe2(fds, O_CLOEXEC);
Py_END_ALLOW_THREADS
res = -1;
errno = ENOSYS;
if (HAVE_PIPE2_RUNTIME) {
Py_BEGIN_ALLOW_THREADS
res = pipe2(fds, O_CLOEXEC);
Py_END_ALLOW_THREADS
}

if (res != 0 && errno == ENOSYS)
{
Expand Down Expand Up @@ -18811,6 +18832,18 @@ posixmodule_exec(PyObject *m)
}
#endif

#if defined(HAVE_PIPE2)
if (HAVE_PIPE2_RUNTIME) {} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard the remaining direct pipe2 call at compile time

When building with an Xcode 27 SDK for a macOS deployment target below 27, removing pipe2 from the module dictionary happens too late to make the build succeed: os_pipe2_impl still calls the availability-annotated pipe2 unguarded at Modules/posixmodule.c:12861, while configure.ac:2797-2802 promotes Clang's unguarded-availability diagnostic to an error. The call needs a lexical availability guard or the same targeted diagnostic suppression used for the runtime-removed preadv/pwritev methods.

Useful? React with 👍 / 👎.

PyObject *dct = PyModule_GetDict(m);
if (dct == NULL) {
return -1;
}
if (PyDict_PopString(dct, "pipe2", NULL) < 0) {
return -1;
}
}
#endif

#ifdef HAVE_STATX
if (statx == NULL) {
PyObject* dct = PyModule_GetDict(m);
Expand Down
Loading