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)