From 90d71a3f499aa3db5761dcb661283cb4abd40970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Mon, 22 Jan 2024 12:37:27 +0100 Subject: [PATCH 01/12] Add configure check for posix_spawn_file_actions_addchdir_np --- configure | 6 ++++++ configure.ac | 2 +- pyconfig.h.in | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 4726b4fe3102ac..785d776d2c4c55 100755 --- a/configure +++ b/configure @@ -20109,6 +20109,12 @@ if test "x$ac_cv_func_posix_spawnp" = xyes then : printf "%s\n" "#define HAVE_POSIX_SPAWNP 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "posix_spawn_file_actions_addchdir_np" "ac_cv_func_posix_spawn_file_actions_addchdir_np" +if test "x$ac_cv_func_posix_spawn_file_actions_addchdir_np" = xyes +then : + printf "%s\n" "#define HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "posix_spawn_file_actions_addclosefrom_np" "ac_cv_func_posix_spawn_file_actions_addclosefrom_np" if test "x$ac_cv_func_posix_spawn_file_actions_addclosefrom_np" = xyes diff --git a/configure.ac b/configure.ac index dd860292cc2058..92325ff19e8596 100644 --- a/configure.ac +++ b/configure.ac @@ -5335,7 +5335,7 @@ AC_CHECK_FUNCS([ \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ pipe2 plock poll ppoll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \ - posix_spawn_file_actions_addclosefrom_np \ + posix_spawn_file_actions_addchdir_np posix_spawn_file_actions_addclosefrom_np \ pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 9da33c954a52f8..73f65ad070973a 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -975,6 +975,10 @@ /* Define to 1 if you have the 'posix_spawnp' function. */ #undef HAVE_POSIX_SPAWNP +/* Define to 1 if you have the 'posix_spawn_file_actions_addchdir_np' + function. */ +#undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP + /* Define to 1 if you have the 'posix_spawn_file_actions_addclosefrom_np' function. */ #undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP From 98825bf5081f4de9402e0c2d46ebad94b7472e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Mon, 22 Jan 2024 12:37:40 +0100 Subject: [PATCH 02/12] Add support for os.POSIX_SPAWN_CHDIR and posix_spawn_file_actions_addchdir_np --- Lib/subprocess.py | 11 ++++++++--- Modules/posixmodule.c | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 7ac2289f535b6d..f15a2280847ddc 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -806,6 +806,7 @@ def _can_use_kqueue(): # These are primarily fail-safe knobs for negatives. A True value does not # guarantee the given libc/syscall API will be used. _USE_POSIX_SPAWN = _use_posix_spawn() +_HAVE_POSIX_SPAWN_CHDIR = hasattr(os, 'POSIX_SPAWN_CHDIR') _HAVE_POSIX_SPAWN_CLOSEFROM = hasattr(os, 'POSIX_SPAWN_CLOSEFROM') @@ -1836,7 +1837,7 @@ def _get_handles(self, stdin, stdout, stderr): errread, errwrite) - def _posix_spawn(self, args, executable, env, restore_signals, close_fds, + def _posix_spawn(self, args, executable, env, restore_signals, close_fds, cwd, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): @@ -1863,6 +1864,9 @@ def _posix_spawn(self, args, executable, env, restore_signals, close_fds, if fd != -1: file_actions.append((os.POSIX_SPAWN_DUP2, fd, fd2)) + if cwd: + file_actions.append((os.POSIX_SPAWN_CHDIR, cwd)) + if close_fds: file_actions.append((os.POSIX_SPAWN_CLOSEFROM, 3)) @@ -1915,7 +1919,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, and preexec_fn is None and (not close_fds or _HAVE_POSIX_SPAWN_CLOSEFROM) and not pass_fds - and cwd is None + and (cwd is None or _HAVE_POSIX_SPAWN_CHDIR) and (p2cread == -1 or p2cread > 2) and (c2pwrite == -1 or c2pwrite > 2) and (errwrite == -1 or errwrite > 2) @@ -1925,7 +1929,8 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, and gids is None and uid is None and umask < 0): - self._posix_spawn(args, executable, env, restore_signals, close_fds, + self._posix_spawn(args, executable, env, restore_signals, + close_fds, cwd, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 07c2b73575f14e..c4aa8f6d45ab2f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7606,6 +7606,9 @@ enum posix_spawn_file_actions_identifier { #ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP ,POSIX_SPAWN_CLOSEFROM #endif +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP + ,POSIX_SPAWN_CHDIR +#endif }; #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) @@ -7864,6 +7867,25 @@ parse_file_actions(PyObject *file_actions, } break; } +#endif +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP + case POSIX_SPAWN_CHDIR: { + PyObject *path; + if (!PyArg_ParseTuple(file_action, "OO&" + ";A chdir file_action tuple must have 2 elements", + &tag_obj, PyUnicode_FSConverter, &path)) + { + goto fail; + } + errno = posix_spawn_file_actions_addchdir_np(file_actionsp, + PyBytes_AS_STRING(path)); + if (errno) { + posix_error(); + Py_DECREF(path); + goto fail; + } + Py_DECREF(path); + break; #endif default: { PyErr_SetString(PyExc_TypeError, @@ -18150,6 +18172,9 @@ all_ins(PyObject *m) #ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP if (PyModule_AddIntMacro(m, POSIX_SPAWN_CLOSEFROM)) return -1; #endif +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP + if (PyModule_AddIntMacro(m, POSIX_SPAWN_CHDIR)) return -1; +#endif #endif #if defined(HAVE_SPAWNV) || defined (HAVE_RTPSPAWN) From 47037acfa5b34ae91f2ce33b23029dff8b489315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Mon, 22 Jan 2024 12:37:54 +0100 Subject: [PATCH 03/12] Add part of documentation --- Doc/library/os.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 7547967c6b32f0..8cc75931a6f19a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -5161,11 +5161,18 @@ written in Python, such as a mail server's external command delivery program. Performs ``os.closerange(fd, INF)``. + .. data:: POSIX_SPAWN_CHDIR + + (``os.POSIX_SPAWN_CHDIR``, *path*) + + Performs ``os.chdir(path)``. + These tuples correspond to the C library :c:func:`!posix_spawn_file_actions_addopen`, :c:func:`!posix_spawn_file_actions_addclose`, - :c:func:`!posix_spawn_file_actions_adddup2`, and - :c:func:`!posix_spawn_file_actions_addclosefrom_np` API calls used to prepare + :c:func:`!posix_spawn_file_actions_adddup2`, + :c:func:`!posix_spawn_file_actions_addclosefrom_np`, and + :c:func:`!posix_spawn_file_actions_addchdir_np` API calls used to prepare for the :c:func:`!posix_spawn` call itself. The *setpgroup* argument will set the process group of the child to the value @@ -5209,8 +5216,11 @@ written in Python, such as a mail server's external command delivery program. .. versionchanged:: 3.13 *env* parameter accepts ``None``. - ``os.POSIX_SPAWN_CLOSEFROM`` is available on platforms where - :c:func:`!posix_spawn_file_actions_addclosefrom_np` exists. + + .. versionchanged:: 3.14 + ``os.POSIX_SPAWN_CLOSEFROM`` and ``os.POSIX_SPAWN_CHDIR`` are available + on platforms where :c:func:`!posix_spawn_file_actions_addclosefrom_np` + and :c:func:`!posix_spawn_file_actions_addchdir_np` exist. .. availability:: Unix, not WASI, not Android, not iOS. From 9bf8852423672375d0444c1129dad641a851986c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Mon, 22 Jan 2024 13:10:41 +0100 Subject: [PATCH 04/12] Minor fix --- Modules/posixmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c4aa8f6d45ab2f..28071ac42d06b5 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7886,6 +7886,7 @@ parse_file_actions(PyObject *file_actions, } Py_DECREF(path); break; + } #endif default: { PyErr_SetString(PyExc_TypeError, From 28d13e59b884a95720e140120a4dcf130103ccbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Tue, 23 Jan 2024 14:30:42 +0100 Subject: [PATCH 05/12] better check for cwd being empty Co-authored-by: Serhiy Storchaka --- Lib/subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index f15a2280847ddc..9a3a2a220c0103 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1864,7 +1864,7 @@ def _posix_spawn(self, args, executable, env, restore_signals, close_fds, cwd, if fd != -1: file_actions.append((os.POSIX_SPAWN_DUP2, fd, fd2)) - if cwd: + if cwd is not None: file_actions.append((os.POSIX_SPAWN_CHDIR, cwd)) if close_fds: From d669339bad2765248bfe51084b59416f5961563d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Thu, 1 Feb 2024 16:18:54 +0100 Subject: [PATCH 06/12] Implement better error reporting for non-existent executable and/or cwd when using posix_spawn. --- Lib/test/test_subprocess.py | 1 + Modules/posixmodule.c | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 0c5679611848ea..691807c325d7c4 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2018,6 +2018,7 @@ def _get_chdir_exception(self): self._nonexistent_dir) return desired_exception + @mock.patch("subprocess._HAVE_POSIX_SPAWN_CHDIR", new=False) def test_exception_cwd(self): """Test error in the child raised in the parent for a bad cwd.""" desired_exception = self._get_chdir_exception() diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 28071ac42d06b5..8c7da4cbbdfc43 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7760,7 +7760,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg static int parse_file_actions(PyObject *file_actions, posix_spawn_file_actions_t *file_actionsp, - PyObject *temp_buffer) + PyObject *temp_buffer, PyObject** cwd) { PyObject *seq; PyObject *file_action = NULL; @@ -7884,7 +7884,8 @@ parse_file_actions(PyObject *file_actions, Py_DECREF(path); goto fail; } - Py_DECREF(path); + Py_XDECREF(cwd); + *cwd = path; break; } #endif @@ -7924,6 +7925,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a Py_ssize_t argc, envc; PyObject *result = NULL; PyObject *temp_buffer = NULL; + PyObject *cwd = NULL; pid_t pid; int err_code; @@ -7995,7 +7997,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (!temp_buffer) { goto exit; } - if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer)) { + if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer, &cwd)) { goto exit; } file_actionsp = &file_actions_buf; @@ -8027,6 +8029,17 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (err_code) { errno = err_code; +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP + if (errno == ENOENT && cwd != NULL) { + /* ENOENT can occur when either the path of the executable or the + * cwd given via file_actions doesn't exist. Since it's not feasible + * to determine which of those paths caused the problem, we return + * an exception with both. */ + PyErr_Format(PyExc_FileNotFoundError, "Either '%S' or '%s' doesn't exist.", + path->object, PyBytes_AS_STRING(cwd)); + goto exit; + } +#endif PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); goto exit; } @@ -8048,6 +8061,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (argvlist) { free_string_array(argvlist, argc); } + Py_XDECREF(cwd); Py_XDECREF(temp_buffer); return result; } From 3184df2380e97d8f2844c034d729304ac2fcf2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 14:38:15 +0200 Subject: [PATCH 07/12] fix reference counting --- Modules/posixmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8c7da4cbbdfc43..faa05228055fd5 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7884,7 +7884,7 @@ parse_file_actions(PyObject *file_actions, Py_DECREF(path); goto fail; } - Py_XDECREF(cwd); + Py_XDECREF(*cwd); *cwd = path; break; } From 8a29643394a96b2f4b5da75b4590572b5edf8301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 14:50:10 +0200 Subject: [PATCH 08/12] use a list to capture all cwds given --- Modules/posixmodule.c | 54 +++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index faa05228055fd5..cecd0a56779042 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7760,7 +7760,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg static int parse_file_actions(PyObject *file_actions, posix_spawn_file_actions_t *file_actionsp, - PyObject *temp_buffer, PyObject** cwd) + PyObject *temp_buffer, PyObject* cwd_buffer) { PyObject *seq; PyObject *file_action = NULL; @@ -7884,8 +7884,10 @@ parse_file_actions(PyObject *file_actions, Py_DECREF(path); goto fail; } - Py_XDECREF(*cwd); - *cwd = path; + if (PyList_Append(cwd_buffer, path)) { + Py_DECREF(path); + goto fail; + } break; } #endif @@ -7925,7 +7927,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a Py_ssize_t argc, envc; PyObject *result = NULL; PyObject *temp_buffer = NULL; - PyObject *cwd = NULL; + PyObject *cwd_buffer = NULL; pid_t pid; int err_code; @@ -7997,7 +7999,12 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (!temp_buffer) { goto exit; } - if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer, &cwd)) { + /* TODO there can be multiple cwd actions .... */ + cwd_buffer = PyList_New(0); + if (!cwd_buffer) { + goto exit; + } + if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer, cwd_buffer)) { goto exit; } file_actionsp = &file_actions_buf; @@ -8030,13 +8037,36 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (err_code) { errno = err_code; #ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP - if (errno == ENOENT && cwd != NULL) { - /* ENOENT can occur when either the path of the executable or the - * cwd given via file_actions doesn't exist. Since it's not feasible - * to determine which of those paths caused the problem, we return - * an exception with both. */ - PyErr_Format(PyExc_FileNotFoundError, "Either '%S' or '%s' doesn't exist.", + Py_ssize_t cwd_size = PyList_GET_SIZE(cwd_buffer); + if (errno == ENOENT && cwd_size > 0) { + /* ENOENT can occur when either the path of the executable or any of + * the cwds given via file_actions doesn't exist. Since it's not + * possible to determine which of those paths caused the problem, + * we return an exception with all of those. */ + + if (cwd_size == 1) { + /* the common case */ + PyObject *cwd = PyList_GET_ITEM(cwd_buffer, 0); + PyErr_Format(PyExc_FileNotFoundError, "Either '%S' or '%s' doesn't exist.", path->object, PyBytes_AS_STRING(cwd)); + } else { + /* TODO ..... */ + PyObject *separator = PyBytes_FromString(", "); + if (!separator) { + goto exit; + } + + PyObject *joined = PyObject_CallMethod(separator, "join", "O", cwd_buffer); + Py_DECREF(separator); + if (!joined) { + goto exit; + } + PyErr_Format(PyExc_FileNotFoundError, + "Either '%S' or one of (%s) doesn't exist.", + path->object, PyBytes_AS_STRING(joined)); + + Py_DECREF(joined); + } goto exit; } #endif @@ -8061,7 +8091,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (argvlist) { free_string_array(argvlist, argc); } - Py_XDECREF(cwd); + Py_XDECREF(cwd_buffer); Py_XDECREF(temp_buffer); return result; } From 12fd85830bbc523b842e1128bf60351005c76765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 14:54:13 +0200 Subject: [PATCH 09/12] use PyBytes_Join rather than .join method call --- Modules/posixmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cecd0a56779042..39a68f80dae68d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8056,7 +8056,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a goto exit; } - PyObject *joined = PyObject_CallMethod(separator, "join", "O", cwd_buffer); + PyObject *joined = PyBytes_Join(separator, cwd_buffer); Py_DECREF(separator); if (!joined) { goto exit; From 30ac0e3037be634fceda05c10320b6f28eb1a101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 15:11:20 +0200 Subject: [PATCH 10/12] update documentation --- Doc/library/os.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 8cc75931a6f19a..d6646b9906aa93 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -5216,11 +5216,12 @@ written in Python, such as a mail server's external command delivery program. .. versionchanged:: 3.13 *env* parameter accepts ``None``. + ``os.POSIX_SPAWN_CLOSEFROM`` is available on platforms where + :c:func:`!posix_spawn_file_actions_addclosefrom_np` exists. - .. versionchanged:: 3.14 - ``os.POSIX_SPAWN_CLOSEFROM`` and ``os.POSIX_SPAWN_CHDIR`` are available - on platforms where :c:func:`!posix_spawn_file_actions_addclosefrom_np` - and :c:func:`!posix_spawn_file_actions_addchdir_np` exist. + .. versionchanged:: 3.15 + ``os.POSIX_SPAWN_CHDIR`` is available on platforms where + :c:func:`!posix_spawn_file_actions_addchdir_np` exist. .. availability:: Unix, not WASI, not Android, not iOS. From c23397546750e5acec9b5410eda6a86586a5a984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 15:21:20 +0200 Subject: [PATCH 11/12] and NEWS file --- .../Library/2026-04-01-15-16-28.gh-issue-114467.g-8r4E.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-04-01-15-16-28.gh-issue-114467.g-8r4E.rst diff --git a/Misc/NEWS.d/next/Library/2026-04-01-15-16-28.gh-issue-114467.g-8r4E.rst b/Misc/NEWS.d/next/Library/2026-04-01-15-16-28.gh-issue-114467.g-8r4E.rst new file mode 100644 index 00000000000000..ba1a04f02c3c07 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-01-15-16-28.gh-issue-114467.g-8r4E.rst @@ -0,0 +1,3 @@ +The :mod:`subprocess` module can now use the :func:`os.posix_spawn` function +with ``cwd`` set on platforms where ``posix_spawn_file_actions_addchdir_np`` +is available. Patch by Jakub Kulik. From f363baa56ab1217f00e6f268ddf128e478be6f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Wed, 1 Apr 2026 15:27:28 +0200 Subject: [PATCH 12/12] update missing comments --- Modules/posixmodule.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 39a68f80dae68d..9070b4092baae6 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7999,7 +7999,8 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (!temp_buffer) { goto exit; } - /* TODO there can be multiple cwd actions .... */ + /* Use a list to capture all directories passed via POSIX_SPAWN_CHDIR + * action for potential exception creation below. */ cwd_buffer = PyList_New(0); if (!cwd_buffer) { goto exit; @@ -8045,12 +8046,13 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a * we return an exception with all of those. */ if (cwd_size == 1) { - /* the common case */ PyObject *cwd = PyList_GET_ITEM(cwd_buffer, 0); PyErr_Format(PyExc_FileNotFoundError, "Either '%S' or '%s' doesn't exist.", path->object, PyBytes_AS_STRING(cwd)); } else { - /* TODO ..... */ + /* Multiple POSIX_SPAWN_CHDIR actions were used in a single + * spawn. In this case, we have to build the expection message + * from all possibly missing paths. */ PyObject *separator = PyBytes_FromString(", "); if (!separator) { goto exit;