mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-31961: subprocess now accepts path-like args (GH-4329)
Allow os.PathLike args in subprocess APIs.
This commit is contained in:
parent
14e976e00e
commit
dd42cb71f2
4 changed files with 55 additions and 8 deletions
|
@ -335,12 +335,12 @@ functions.
|
||||||
the class uses the Windows ``CreateProcess()`` function. The arguments to
|
the class uses the Windows ``CreateProcess()`` function. The arguments to
|
||||||
:class:`Popen` are as follows.
|
:class:`Popen` are as follows.
|
||||||
|
|
||||||
*args* should be a sequence of program arguments or else a single string.
|
*args* should be a sequence of program arguments or else a single string or
|
||||||
By default, the program to execute is the first item in *args* if *args* is
|
:term:`path-like object`. By default, the program to execute is the first
|
||||||
a sequence. If *args* is a string, the interpretation is
|
item in *args* if *args* is a sequence. If *args* is a string, the
|
||||||
platform-dependent and described below. See the *shell* and *executable*
|
interpretation is platform-dependent and described below. See the *shell*
|
||||||
arguments for additional differences from the default behavior. Unless
|
and *executable* arguments for additional differences from the default
|
||||||
otherwise stated, it is recommended to pass *args* as a sequence.
|
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
|
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
|
path of the program to execute. However, this can only be done if not
|
||||||
|
@ -551,6 +551,10 @@ functions.
|
||||||
Popen destructor now emits a :exc:`ResourceWarning` warning if the child
|
Popen destructor now emits a :exc:`ResourceWarning` warning if the child
|
||||||
process is still running.
|
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
|
Exceptions
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
|
@ -1097,6 +1097,11 @@ class Popen(object):
|
||||||
assert not pass_fds, "pass_fds not supported on Windows."
|
assert not pass_fds, "pass_fds not supported on Windows."
|
||||||
|
|
||||||
if not isinstance(args, str):
|
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)
|
args = list2cmdline(args)
|
||||||
|
|
||||||
# Process startup details
|
# Process startup details
|
||||||
|
@ -1369,7 +1374,10 @@ class Popen(object):
|
||||||
if isinstance(args, (str, bytes)):
|
if isinstance(args, (str, bytes)):
|
||||||
args = [args]
|
args = [args]
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
args = list(args)
|
args = list(args)
|
||||||
|
except TypeError: # os.PathLike instead of a sequence?
|
||||||
|
args = [os.fsencode(args)] # os.PathLike -> [str]
|
||||||
|
|
||||||
if shell:
|
if shell:
|
||||||
# On Android the default shell is at '/system/bin/sh'.
|
# On Android the default shell is at '/system/bin/sh'.
|
||||||
|
|
|
@ -1475,6 +1475,37 @@ class RunFuncTestCase(BaseTestCase):
|
||||||
env=newenv)
|
env=newenv)
|
||||||
self.assertEqual(cp.returncode, 33)
|
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):
|
def test_capture_output(self):
|
||||||
cp = self.run_python(("import sys;"
|
cp = self.run_python(("import sys;"
|
||||||
"sys.stdout.write('BDFL'); "
|
"sys.stdout.write('BDFL'); "
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
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.
|
Loading…
Add table
Add a link
Reference in a new issue