From 9e872399e2ab4712da3729352d85dcabbdf08675 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 26 Feb 2018 23:49:18 +0200 Subject: [PATCH 1/9] bpo-31961: Fix support of path-like executables in subprocess. --- Lib/subprocess.py | 25 +++++++++++++---------- Lib/test/test_subprocess.py | 40 ++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 2723bc9e4274c3..1798acc83431d8 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1096,13 +1096,17 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, assert not pass_fds, "pass_fds not supported on Windows." - if not isinstance(args, str): - try: - args = os.fsdecode(args) # os.PathLike -> str - except TypeError: # not an os.PathLike, must be a sequence. - args = list(args) - args[0] = os.fsdecode(args[0]) # os.PathLike -> str - args = list2cmdline(args) + if isinstance(args, str): + pass + elif not shell and isinstance(args, os.PathLike): + args = os.fsdecode(args) + else: + args = list(args) + args[0] = os.fsdecode(args[0]) + args = list2cmdline(args) + + if executable is not None: + executable = os.fsdecode(executable) # Process startup details if startupinfo is None: @@ -1373,11 +1377,10 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if isinstance(args, (str, bytes)): args = [args] + elif not shell and isinstance(args, os.PathLike): + args = [os.fsencode(args)] else: - try: - args = list(args) - except TypeError: # os.PathLike instead of a sequence? - args = [os.fsencode(args)] # os.PathLike -> [str] + args = list(args) if shell: # On Android the default shell is at '/system/bin/sh'. diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index b3ccb0de63a84c..8132c8c1361b6f 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -17,6 +17,7 @@ import threading import gc import textwrap +from test.test_os import _PathLike try: import ctypes @@ -302,6 +303,12 @@ def test_executable(self): "doesnotexist") self._assert_python([doesnotexist, "-c"], executable=sys.executable) + def test_pathlike_executable(self): + doesnotexist = os.path.join(os.path.dirname(sys.executable), + "doesnotexist") + self._assert_python([doesnotexist, "-c"], + executable=_PathLike(sys.executable)) + def test_executable_takes_precedence(self): # Check that the executable argument takes precedence over args[0]. # @@ -318,6 +325,11 @@ def test_executable_replaces_shell(self): # when shell=True. self._assert_python([], executable=sys.executable, shell=True) + @unittest.skipIf(mswindows, "executable argument replaces shell") + def test_pathlike_executable_replaces_shell(self): + self._assert_python([], executable=_PathLike(sys.executable), + shell=True) + # For use in the test_cwd* tests below. def _normalize_cwd(self, cwd): # Normalize an expected cwd (for Tru64 support). @@ -1477,33 +1489,19 @@ def test_run_kwargs(self): def test_run_with_pathlike_path(self): # bpo-31961: test run(pathlike_object) - class Path: - def __fspath__(self): - # the name of a command that can be run without - # any argumenets that exit fast - return 'dir' if mswindows else 'ls' - - path = Path() - if mswindows: - res = subprocess.run(path, stdout=subprocess.DEVNULL, shell=True) - else: - res = subprocess.run(path, stdout=subprocess.DEVNULL) - + # the name of a command that can be run without + # any argumenets that exit fast + path = _PathLike('tree.com' if mswindows else 'ls') + res = subprocess.run(path, stdout=subprocess.DEVNULL) self.assertEqual(res.returncode, 0) + with self.assertRaises(TypeError): + subprocess.run(path, stdout=subprocess.DEVNULL, shell=True) def test_run_with_pathlike_path_and_arguments(self): # bpo-31961: test run([pathlike_object, 'additional arguments']) - class Path: - def __fspath__(self): - # the name of a command that can be run without - # any argumenets that exits fast - return sys.executable - - path = Path() - + path = _PathLike(sys.executable) args = [path, '-c', 'import sys; sys.exit(57)'] res = subprocess.run(args) - self.assertEqual(res.returncode, 57) def test_capture_output(self): From 8cce34419be30abad3174446b1c4848e3052e134 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 27 Feb 2018 08:53:34 +0200 Subject: [PATCH 2/9] More changes: * Simplify the code on Posix * Fix the code on Windows. * Use absolute paths in tests. --- Lib/subprocess.py | 7 +++---- Lib/test/test_subprocess.py | 6 +++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 1798acc83431d8..57bd6b7c40113f 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1099,7 +1099,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if isinstance(args, str): pass elif not shell and isinstance(args, os.PathLike): - args = os.fsdecode(args) + args = list2cmdline([os.fsdecode(args)]) else: args = list(args) args[0] = os.fsdecode(args[0]) @@ -1375,10 +1375,9 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, restore_signals, start_new_session): """Execute program (POSIX version)""" - if isinstance(args, (str, bytes)): + if (isinstance(args, (str, bytes)) or + not shell and isinstance(args, os.PathLike)): args = [args] - elif not shell and isinstance(args, os.PathLike): - args = [os.fsencode(args)] else: args = list(args) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 8132c8c1361b6f..b60782cb2ee4db 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1491,7 +1491,11 @@ def test_run_with_pathlike_path(self): # bpo-31961: test run(pathlike_object) # the name of a command that can be run without # any argumenets that exit fast - path = _PathLike('tree.com' if mswindows else 'ls') + prog = 'tree.com' if mswindows else 'ls' + path = shutil.which(prog) + if path is None: + self.skipTest(f'{prog} required for this test') + path = _PathLike(path) res = subprocess.run(path, stdout=subprocess.DEVNULL) self.assertEqual(res.returncode, 0) with self.assertRaises(TypeError): From 0b4e876054067da873d70fe33e6f572f499b1a1c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 28 Feb 2018 16:05:40 +0200 Subject: [PATCH 3/9] Revert "Revert "bpo-31961: subprocess now accepts path-like args (GH-4329)" (#5912)" This reverts commit be50a7b627d0aa37e08fa8e2d5568891f19903ce. --- Doc/library/subprocess.rst | 16 ++++++++++------ Lib/subprocess.py | 12 ++++++++++-- Lib/test/test_subprocess.py | 31 +++++++++++++++++++++++++++++++ Misc/NEWS.d/3.7.0b1.rst | 1 - 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index db7a88af6bea08..a22afe041850bb 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -339,12 +339,12 @@ functions. the class uses the Windows ``CreateProcess()`` function. The arguments to :class:`Popen` are as follows. - *args* should be a sequence of program arguments or else a single string. - By default, the program to execute is the first item in *args* if *args* is - a sequence. If *args* is a string, the interpretation is - platform-dependent and described below. See the *shell* and *executable* - arguments for additional differences from the default behavior. Unless - otherwise stated, it is recommended to pass *args* as a sequence. + *args* should be a sequence of program arguments or else a single string or + :term:`path-like object`. By default, the program to execute is the first + item in *args* if *args* is a sequence. If *args* is a string, the + interpretation is platform-dependent and described below. See the *shell* + and *executable* arguments for additional differences from the default + behavior. Unless otherwise stated, it is recommended to pass *args* as a sequence. On POSIX, if *args* is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not @@ -558,6 +558,10 @@ functions. Popen destructor now emits a :exc:`ResourceWarning` warning if the child process is still running. + .. versionchanged:: 3.7 + *args*, or the first element of *args* if *args* is a sequence, can now + be a :term:`path-like object`. + Exceptions ^^^^^^^^^^ diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 93635ee61f7e9f..2723bc9e4274c3 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1097,7 +1097,12 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, assert not pass_fds, "pass_fds not supported on Windows." if not isinstance(args, str): - args = list2cmdline(args) + try: + args = os.fsdecode(args) # os.PathLike -> str + except TypeError: # not an os.PathLike, must be a sequence. + args = list(args) + args[0] = os.fsdecode(args[0]) # os.PathLike -> str + args = list2cmdline(args) # Process startup details if startupinfo is None: @@ -1369,7 +1374,10 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if isinstance(args, (str, bytes)): args = [args] else: - args = list(args) + try: + args = list(args) + except TypeError: # os.PathLike instead of a sequence? + args = [os.fsencode(args)] # os.PathLike -> [str] if shell: # On Android the default shell is at '/system/bin/sh'. diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 46cb5f117e84f4..b3ccb0de63a84c 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1475,6 +1475,37 @@ def test_run_kwargs(self): env=newenv) self.assertEqual(cp.returncode, 33) + def test_run_with_pathlike_path(self): + # bpo-31961: test run(pathlike_object) + class Path: + def __fspath__(self): + # the name of a command that can be run without + # any argumenets that exit fast + return 'dir' if mswindows else 'ls' + + path = Path() + if mswindows: + res = subprocess.run(path, stdout=subprocess.DEVNULL, shell=True) + else: + res = subprocess.run(path, stdout=subprocess.DEVNULL) + + self.assertEqual(res.returncode, 0) + + def test_run_with_pathlike_path_and_arguments(self): + # bpo-31961: test run([pathlike_object, 'additional arguments']) + class Path: + def __fspath__(self): + # the name of a command that can be run without + # any argumenets that exits fast + return sys.executable + + path = Path() + + args = [path, '-c', 'import sys; sys.exit(57)'] + res = subprocess.run(args) + + self.assertEqual(res.returncode, 57) + def test_capture_output(self): cp = self.run_python(("import sys;" "sys.stdout.write('BDFL'); " diff --git a/Misc/NEWS.d/3.7.0b1.rst b/Misc/NEWS.d/3.7.0b1.rst index ec7b3c8ecf78c4..bd3a6111df0b75 100644 --- a/Misc/NEWS.d/3.7.0b1.rst +++ b/Misc/NEWS.d/3.7.0b1.rst @@ -630,7 +630,6 @@ Add contextlib.AsyncExitStack. Patch by Alexander Mohr and Ilya Kulakov. .. nonce: x5Sv0R .. section: Library -*Removed in Python 3.7.0b2.* The *args* argument of subprocess.Popen can now be a :term:`path-like object`. If *args* is given as a sequence, it's first element can now be a :term:`path-like object` as well. From ccd34641d34e1d031df9aedb04ba89c61529a68e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 28 Feb 2018 16:08:27 +0200 Subject: [PATCH 4/9] Restore NEWS. --- Misc/NEWS.d/3.7.0b1.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/NEWS.d/3.7.0b1.rst b/Misc/NEWS.d/3.7.0b1.rst index bd3a6111df0b75..ec7b3c8ecf78c4 100644 --- a/Misc/NEWS.d/3.7.0b1.rst +++ b/Misc/NEWS.d/3.7.0b1.rst @@ -630,6 +630,7 @@ Add contextlib.AsyncExitStack. Patch by Alexander Mohr and Ilya Kulakov. .. nonce: x5Sv0R .. section: Library +*Removed in Python 3.7.0b2.* The *args* argument of subprocess.Popen can now be a :term:`path-like object`. If *args* is given as a sequence, it's first element can now be a :term:`path-like object` as well. From cee318b7eb6edb76b17ce7a9a1e341b1fa495c63 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 27 Mar 2018 13:19:27 +0300 Subject: [PATCH 5/9] Minimize docs changes. --- Doc/library/subprocess.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index a22afe041850bb..4ea3ac182fda66 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -340,11 +340,12 @@ functions. :class:`Popen` are as follows. *args* should be a sequence of program arguments or else a single string or - :term:`path-like object`. By default, the program to execute is the first - item in *args* if *args* is a sequence. If *args* is a string, the - interpretation is platform-dependent and described below. See the *shell* - and *executable* arguments for additional differences from the default - behavior. Unless otherwise stated, it is recommended to pass *args* as a sequence. + :term:`path-like object`. + By default, the program to execute is the first item in *args* if *args* is + a sequence. If *args* is a string, the interpretation is + platform-dependent and described below. See the *shell* and *executable* + arguments for additional differences from the default behavior. Unless + otherwise stated, it is recommended to pass *args* as a sequence. On POSIX, if *args* is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not From 5c0dd882b7c4e5a1542b7f3b0a469deb6a289980 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 27 Mar 2018 13:28:47 +0300 Subject: [PATCH 6/9] Retarget to 3.8 and add a news entry. --- Doc/library/subprocess.rst | 2 +- .../next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 3fb7b3bd3be124..190243cdf80063 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -562,7 +562,7 @@ functions. Popen destructor now emits a :exc:`ResourceWarning` warning if the child process is still running. - .. versionchanged:: 3.7 + .. versionchanged:: 3.8 *args*, or the first element of *args* if *args* is a sequence, can now be a :term:`path-like object`. diff --git a/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst new file mode 100644 index 00000000000000..0167ea5b93dc19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst @@ -0,0 +1,3 @@ +The *args* argument of :func:`subprocess.Popen`, or the first element of +*args* if *args* is a sequence, can now be a :term:`path-like object`. Based +on patch by Anders Lorentsen. From 6a8d394b30301289486f128bb7cfda02adc93d9c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Oct 2018 23:58:19 +0200 Subject: [PATCH 7/9] Add support for bytes and path-like args. --- Doc/library/subprocess.rst | 34 ++++++++++++++----- Lib/subprocess.py | 8 ++--- Lib/test/test_subprocess.py | 23 +++++++++++++ .../2018-03-27-13-28-16.bpo-31961.GjLoYu.rst | 9 +++-- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 3cc7dacb116676..0b36bf47be8992 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -344,8 +344,8 @@ functions. the class uses the Windows ``CreateProcess()`` function. The arguments to :class:`Popen` are as follows. - *args* should be a sequence of program arguments or else a single string or - :term:`path-like object`. + *args* should be a sequence of program arguments or else a single string + or :term:`path-like object`. By default, the program to execute is the first item in *args* if *args* is a sequence. If *args* is a string, the interpretation is platform-dependent and described below. See the *shell* and *executable* @@ -379,6 +379,15 @@ functions. manner described in :ref:`converting-argument-sequence`. This is because the underlying ``CreateProcess()`` operates on strings. + .. versionchanged:: 3.6 + *args* parameter accepts a :term:`path-like object` if *shell* is + ``False`` and a sequence containing path-like objects on POSIX. + + .. versionchanged:: 3.8 + *args* parameter accepts a :term:`path-like object` if *shell* is + ``False`` and a sequence containing bytes and path-like objects + on Windows. + The *shell* argument (which defaults to ``False``) specifies whether to use the shell as the program to execute. If *shell* is ``True``, it is recommended to pass *args* as a string rather than as a sequence. @@ -434,6 +443,13 @@ functions. :program:`ps`. If ``shell=True``, on POSIX the *executable* argument specifies a replacement shell for the default :file:`/bin/sh`. + .. versionchanged:: 3.6 + *executable* parameter accepts a :term:`path-like object` on POSIX. + + .. versionchanged:: 3.8 + *executable* parameter accepts a bytes and :term:`path-like object` + on Windows. + *stdin*, *stdout* and *stderr* specify the executed program's standard input, standard output and standard error file handles, respectively. Valid values are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive @@ -490,13 +506,19 @@ functions. The *pass_fds* parameter was added. If *cwd* is not ``None``, the function changes the working directory to - *cwd* before executing the child. *cwd* can be a :class:`str` and + *cwd* before executing the child. *cwd* can be a string, bytes or :term:`path-like ` object. In particular, the function looks for *executable* (or for the first item in *args*) relative to *cwd* if the executable path is a relative path. .. versionchanged:: 3.6 - *cwd* parameter accepts a :term:`path-like object`. + *cwd* parameter accepts a :term:`path-like object` on POSIX. + + .. versionchanged:: 3.7 + *cwd* parameter accepts a :term:`path-like object` on Windows. + + .. versionchanged:: 3.8 + *cwd* parameter accepts a bytes object on Windows. If *restore_signals* is true (the default) all signals that Python has set to SIG_IGN are restored to SIG_DFL in the child process before the exec. @@ -567,10 +589,6 @@ functions. Popen destructor now emits a :exc:`ResourceWarning` warning if the child process is still running. - .. versionchanged:: 3.8 - *args*, or the first element of *args* if *args* is a sequence, can now - be a :term:`path-like object`. - Exceptions ^^^^^^^^^^ diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 7d8f1d5590a6e6..833d8ea823aeda 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -515,7 +515,7 @@ def list2cmdline(seq): # "Parsing C++ Command-Line Arguments" result = [] needquote = False - for arg in seq: + for arg in map(os.fsdecode, seq): bs_buf = [] # Add a space to separate this argument from the others @@ -1122,10 +1122,8 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if isinstance(args, str): pass elif not shell and isinstance(args, os.PathLike): - args = list2cmdline([os.fsdecode(args)]) + args = list2cmdline([args]) else: - args = list(args) - args[0] = os.fsdecode(args[0]) args = list2cmdline(args) if executable is not None: @@ -1187,7 +1185,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, int(not close_fds), creationflags, env, - os.fspath(cwd) if cwd is not None else None, + os.fsdecode(cwd) if cwd is not None else None, startupinfo) finally: # Child is launched. Close the parent's copy of those pipe diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 2541f5aec1968c..fca3ed62099bde 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -304,6 +304,12 @@ def test_executable(self): "doesnotexist") self._assert_python([doesnotexist, "-c"], executable=sys.executable) + def test_bytes_executable(self): + doesnotexist = os.path.join(os.path.dirname(sys.executable), + "doesnotexist") + self._assert_python([doesnotexist, "-c"], + executable=os.fsencode(sys.executable)) + def test_pathlike_executable(self): doesnotexist = os.path.join(os.path.dirname(sys.executable), "doesnotexist") @@ -326,6 +332,11 @@ def test_executable_replaces_shell(self): # when shell=True. self._assert_python([], executable=sys.executable, shell=True) + @unittest.skipIf(mswindows, "executable argument replaces shell") + def test_bytes_executable_replaces_shell(self): + self._assert_python([], executable=os.fsencode(sys.executable), + shell=True) + @unittest.skipIf(mswindows, "executable argument replaces shell") def test_pathlike_executable_replaces_shell(self): self._assert_python([], executable=FakePath(sys.executable), @@ -369,6 +380,11 @@ def test_cwd(self): temp_dir = self._normalize_cwd(temp_dir) self._assert_cwd(temp_dir, sys.executable, cwd=temp_dir) + def test_cwd_with_bytes(self): + temp_dir = tempfile.gettempdir() + temp_dir = self._normalize_cwd(temp_dir) + self._assert_cwd(temp_dir, sys.executable, cwd=os.fsencode(temp_dir)) + def test_cwd_with_pathlike(self): temp_dir = tempfile.gettempdir() temp_dir = self._normalize_cwd(temp_dir) @@ -1498,6 +1514,13 @@ def test_run_with_pathlike_path(self): with self.assertRaises(TypeError): subprocess.run(path, stdout=subprocess.DEVNULL, shell=True) + def test_run_with_bytes_path_and_arguments(self): + # bpo-31961: test run([bytes_object, b'additional arguments']) + path = os.fsencode(sys.executable) + args = [path, '-c', b'import sys; sys.exit(57)'] + res = subprocess.run(args) + self.assertEqual(res.returncode, 57) + def test_run_with_pathlike_path_and_arguments(self): # bpo-31961: test run([pathlike_object, 'additional arguments']) path = FakePath(sys.executable) diff --git a/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst index 0167ea5b93dc19..8113a7e68bc1a4 100644 --- a/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst +++ b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst @@ -1,3 +1,6 @@ -The *args* argument of :func:`subprocess.Popen`, or the first element of -*args* if *args* is a sequence, can now be a :term:`path-like object`. Based -on patch by Anders Lorentsen. +Added support for bytes and path-like objects in :func:`subprocess.Popen` +on Windows. *args* parameter accepts now a :term:`path-like object` if +*shell* is ``False`` and a sequence containing bytes and path-like objects. +*executable* parameter accepts now a bytes and :term:`path-like object`. +*cwd* parameter accepts now a bytes object. +Based on patch by Anders Lorentsen. From 7d51e791be0bd0a4e318c9ed95bb0cef73342c7c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 18 Nov 2018 19:51:45 +0200 Subject: [PATCH 8/9] Improve error messages for invalid args. --- Lib/subprocess.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 833d8ea823aeda..c8c6437ffc8cd7 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1121,7 +1121,14 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, if isinstance(args, str): pass - elif not shell and isinstance(args, os.PathLike): + elif isinstance(args, bytes): + if shell: + raise TypeError('bytes args is not allowed on Windows') + args = list2cmdline([args]) + elif isinstance(args, os.PathLike): + if shell: + raise TypeError('path-like args is not allowed when ' + 'shell is true') args = list2cmdline([args]) else: args = list2cmdline(args) @@ -1400,8 +1407,12 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, restore_signals, start_new_session): """Execute program (POSIX version)""" - if (isinstance(args, (str, bytes)) or - not shell and isinstance(args, os.PathLike)): + if isinstance(args, (str, bytes)): + args = [args] + elif isinstance(args, os.PathLike): + if shell: + raise TypeError('path-like args is not allowed when ' + 'shell is true') args = [args] else: args = list(args) From 1073a0f2d786dbdbadaacc6f675c1fcf3366973b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 18 May 2019 00:38:56 -0700 Subject: [PATCH 9/9] Fix the NEWS wording to be correct English. --- .../next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst index 8113a7e68bc1a4..a38db6790f47ec 100644 --- a/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst +++ b/Misc/NEWS.d/next/Library/2018-03-27-13-28-16.bpo-31961.GjLoYu.rst @@ -1,6 +1,6 @@ Added support for bytes and path-like objects in :func:`subprocess.Popen` -on Windows. *args* parameter accepts now a :term:`path-like object` if +on Windows. The *args* parameter now accepts a :term:`path-like object` if *shell* is ``False`` and a sequence containing bytes and path-like objects. -*executable* parameter accepts now a bytes and :term:`path-like object`. -*cwd* parameter accepts now a bytes object. +The *executable* parameter now accepts a bytes and :term:`path-like object`. +The *cwd* parameter now accepts a bytes object. Based on patch by Anders Lorentsen.