Fixes Issue #16114: The subprocess module no longer provides a
misleading error message stating that args[0] did not exist when
either the cwd or executable keyword arguments specified a path that
did not exist.
It now keeps track of if the child got as far as preexec and reports it if
not back to the parent via a special "noexec" error message value in
the error pipe so that the cwd can be blamed for a failed chdir
instead of the exec of the executable being blamed instead.
The executable is also always reported accurately when exec fails.
Unittests enhanced to cover these cases.
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 93262df..83c79ef 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -1169,6 +1169,7 @@
if executable is None:
executable = args[0]
+ orig_executable = executable
# For transferring possible exec failure from child to parent.
# Data format: "exception name:hex errno:description"
@@ -1224,6 +1225,7 @@
self._child_created = True
if self.pid == 0:
# Child
+ reached_preexec = False
try:
# Close parent's pipe ends
if p2cwrite != -1:
@@ -1288,6 +1290,7 @@
if start_new_session and hasattr(os, 'setsid'):
os.setsid()
+ reached_preexec = True
if preexec_fn:
preexec_fn()
@@ -1303,6 +1306,8 @@
errno_num = exc_value.errno
else:
errno_num = 0
+ if not reached_preexec:
+ exc_value = "noexec"
message = '%s:%x:%s' % (exc_type.__name__,
errno_num, exc_value)
message = message.encode(errors="surrogatepass")
@@ -1364,10 +1369,17 @@
err_msg = err_msg.decode(errors="surrogatepass")
if issubclass(child_exception_type, OSError) and hex_errno:
errno_num = int(hex_errno, 16)
+ child_exec_never_called = (err_msg == "noexec")
+ if child_exec_never_called:
+ err_msg = ""
if errno_num != 0:
err_msg = os.strerror(errno_num)
if errno_num == errno.ENOENT:
- err_msg += ': ' + repr(args[0])
+ if child_exec_never_called:
+ # The error must be from chdir(cwd).
+ err_msg += ': ' + repr(cwd)
+ else:
+ err_msg += ': ' + repr(orig_executable)
raise child_exception_type(errno_num, err_msg)
raise child_exception_type(err_msg)