Allow os.PathLike args in subprocess APIs.
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
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
^^^^^^^^^^
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:
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'.
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'); "
--- /dev/null
+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.