mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-75729: Fix os.spawn tests not handling spaces on Windows (#99150)
* Quote paths in os.spawn tests on Windows so they work with spaces * Add NEWS entry for os spawn test fix * Fix code style to avoid double negative in os.spawn tests Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> --------- Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
13774969f7
commit
a34c796238
2 changed files with 59 additions and 45 deletions
|
@ -3214,6 +3214,14 @@ class PidTests(unittest.TestCase):
|
||||||
|
|
||||||
@support.requires_subprocess()
|
@support.requires_subprocess()
|
||||||
class SpawnTests(unittest.TestCase):
|
class SpawnTests(unittest.TestCase):
|
||||||
|
@staticmethod
|
||||||
|
def quote_args(args):
|
||||||
|
# On Windows, os.spawn* simply joins arguments with spaces:
|
||||||
|
# arguments need to be quoted
|
||||||
|
if os.name != 'nt':
|
||||||
|
return args
|
||||||
|
return [f'"{arg}"' if " " in arg.strip() else arg for arg in args]
|
||||||
|
|
||||||
def create_args(self, *, with_env=False, use_bytes=False):
|
def create_args(self, *, with_env=False, use_bytes=False):
|
||||||
self.exitcode = 17
|
self.exitcode = 17
|
||||||
|
|
||||||
|
@ -3234,115 +3242,118 @@ class SpawnTests(unittest.TestCase):
|
||||||
with open(filename, "w", encoding="utf-8") as fp:
|
with open(filename, "w", encoding="utf-8") as fp:
|
||||||
fp.write(code)
|
fp.write(code)
|
||||||
|
|
||||||
args = [sys.executable, filename]
|
program = sys.executable
|
||||||
|
args = self.quote_args([program, filename])
|
||||||
if use_bytes:
|
if use_bytes:
|
||||||
|
program = os.fsencode(program)
|
||||||
args = [os.fsencode(a) for a in args]
|
args = [os.fsencode(a) for a in args]
|
||||||
self.env = {os.fsencode(k): os.fsencode(v)
|
self.env = {os.fsencode(k): os.fsencode(v)
|
||||||
for k, v in self.env.items()}
|
for k, v in self.env.items()}
|
||||||
|
|
||||||
return args
|
return program, args
|
||||||
|
|
||||||
@requires_os_func('spawnl')
|
@requires_os_func('spawnl')
|
||||||
def test_spawnl(self):
|
def test_spawnl(self):
|
||||||
args = self.create_args()
|
program, args = self.create_args()
|
||||||
exitcode = os.spawnl(os.P_WAIT, args[0], *args)
|
exitcode = os.spawnl(os.P_WAIT, program, *args)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnle')
|
@requires_os_func('spawnle')
|
||||||
def test_spawnle(self):
|
def test_spawnle(self):
|
||||||
args = self.create_args(with_env=True)
|
program, args = self.create_args(with_env=True)
|
||||||
exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env)
|
exitcode = os.spawnle(os.P_WAIT, program, *args, self.env)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnlp')
|
@requires_os_func('spawnlp')
|
||||||
def test_spawnlp(self):
|
def test_spawnlp(self):
|
||||||
args = self.create_args()
|
program, args = self.create_args()
|
||||||
exitcode = os.spawnlp(os.P_WAIT, args[0], *args)
|
exitcode = os.spawnlp(os.P_WAIT, program, *args)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnlpe')
|
@requires_os_func('spawnlpe')
|
||||||
def test_spawnlpe(self):
|
def test_spawnlpe(self):
|
||||||
args = self.create_args(with_env=True)
|
program, args = self.create_args(with_env=True)
|
||||||
exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env)
|
exitcode = os.spawnlpe(os.P_WAIT, program, *args, self.env)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnv')
|
@requires_os_func('spawnv')
|
||||||
def test_spawnv(self):
|
def test_spawnv(self):
|
||||||
args = self.create_args()
|
program, args = self.create_args()
|
||||||
exitcode = os.spawnv(os.P_WAIT, args[0], args)
|
exitcode = os.spawnv(os.P_WAIT, program, args)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
# Test for PyUnicode_FSConverter()
|
# Test for PyUnicode_FSConverter()
|
||||||
exitcode = os.spawnv(os.P_WAIT, FakePath(args[0]), args)
|
exitcode = os.spawnv(os.P_WAIT, FakePath(program), args)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnve')
|
@requires_os_func('spawnve')
|
||||||
def test_spawnve(self):
|
def test_spawnve(self):
|
||||||
args = self.create_args(with_env=True)
|
program, args = self.create_args(with_env=True)
|
||||||
exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env)
|
exitcode = os.spawnve(os.P_WAIT, program, args, self.env)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnvp')
|
@requires_os_func('spawnvp')
|
||||||
def test_spawnvp(self):
|
def test_spawnvp(self):
|
||||||
args = self.create_args()
|
program, args = self.create_args()
|
||||||
exitcode = os.spawnvp(os.P_WAIT, args[0], args)
|
exitcode = os.spawnvp(os.P_WAIT, program, args)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnvpe')
|
@requires_os_func('spawnvpe')
|
||||||
def test_spawnvpe(self):
|
def test_spawnvpe(self):
|
||||||
args = self.create_args(with_env=True)
|
program, args = self.create_args(with_env=True)
|
||||||
exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env)
|
exitcode = os.spawnvpe(os.P_WAIT, program, args, self.env)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnv')
|
@requires_os_func('spawnv')
|
||||||
def test_nowait(self):
|
def test_nowait(self):
|
||||||
args = self.create_args()
|
program, args = self.create_args()
|
||||||
pid = os.spawnv(os.P_NOWAIT, args[0], args)
|
pid = os.spawnv(os.P_NOWAIT, program, args)
|
||||||
support.wait_process(pid, exitcode=self.exitcode)
|
support.wait_process(pid, exitcode=self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnve')
|
@requires_os_func('spawnve')
|
||||||
def test_spawnve_bytes(self):
|
def test_spawnve_bytes(self):
|
||||||
# Test bytes handling in parse_arglist and parse_envlist (#28114)
|
# Test bytes handling in parse_arglist and parse_envlist (#28114)
|
||||||
args = self.create_args(with_env=True, use_bytes=True)
|
program, args = self.create_args(with_env=True, use_bytes=True)
|
||||||
exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env)
|
exitcode = os.spawnve(os.P_WAIT, program, args, self.env)
|
||||||
self.assertEqual(exitcode, self.exitcode)
|
self.assertEqual(exitcode, self.exitcode)
|
||||||
|
|
||||||
@requires_os_func('spawnl')
|
@requires_os_func('spawnl')
|
||||||
def test_spawnl_noargs(self):
|
def test_spawnl_noargs(self):
|
||||||
args = self.create_args()
|
program, __ = self.create_args()
|
||||||
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0])
|
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program)
|
||||||
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0], '')
|
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program, '')
|
||||||
|
|
||||||
@requires_os_func('spawnle')
|
@requires_os_func('spawnle')
|
||||||
def test_spawnle_noargs(self):
|
def test_spawnle_noargs(self):
|
||||||
args = self.create_args()
|
program, __ = self.create_args()
|
||||||
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], {})
|
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, {})
|
||||||
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], '', {})
|
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, '', {})
|
||||||
|
|
||||||
@requires_os_func('spawnv')
|
@requires_os_func('spawnv')
|
||||||
def test_spawnv_noargs(self):
|
def test_spawnv_noargs(self):
|
||||||
args = self.create_args()
|
program, __ = self.create_args()
|
||||||
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ())
|
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ())
|
||||||
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], [])
|
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, [])
|
||||||
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ('',))
|
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ('',))
|
||||||
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], [''])
|
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, [''])
|
||||||
|
|
||||||
@requires_os_func('spawnve')
|
@requires_os_func('spawnve')
|
||||||
def test_spawnve_noargs(self):
|
def test_spawnve_noargs(self):
|
||||||
args = self.create_args()
|
program, __ = self.create_args()
|
||||||
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], (), {})
|
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, (), {})
|
||||||
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [], {})
|
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, [], {})
|
||||||
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], ('',), {})
|
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, ('',), {})
|
||||||
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [''], {})
|
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, [''], {})
|
||||||
|
|
||||||
def _test_invalid_env(self, spawn):
|
def _test_invalid_env(self, spawn):
|
||||||
args = [sys.executable, '-c', 'pass']
|
program = sys.executable
|
||||||
|
args = self.quote_args([program, '-c', 'pass'])
|
||||||
|
|
||||||
# null character in the environment variable name
|
# null character in the environment variable name
|
||||||
newenv = os.environ.copy()
|
newenv = os.environ.copy()
|
||||||
newenv["FRUIT\0VEGETABLE"] = "cabbage"
|
newenv["FRUIT\0VEGETABLE"] = "cabbage"
|
||||||
try:
|
try:
|
||||||
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
|
exitcode = spawn(os.P_WAIT, program, args, newenv)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -3352,7 +3363,7 @@ class SpawnTests(unittest.TestCase):
|
||||||
newenv = os.environ.copy()
|
newenv = os.environ.copy()
|
||||||
newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
|
newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
|
||||||
try:
|
try:
|
||||||
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
|
exitcode = spawn(os.P_WAIT, program, args, newenv)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -3362,7 +3373,7 @@ class SpawnTests(unittest.TestCase):
|
||||||
newenv = os.environ.copy()
|
newenv = os.environ.copy()
|
||||||
newenv["FRUIT=ORANGE"] = "lemon"
|
newenv["FRUIT=ORANGE"] = "lemon"
|
||||||
try:
|
try:
|
||||||
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
|
exitcode = spawn(os.P_WAIT, program, args, newenv)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -3375,10 +3386,11 @@ class SpawnTests(unittest.TestCase):
|
||||||
fp.write('import sys, os\n'
|
fp.write('import sys, os\n'
|
||||||
'if os.getenv("FRUIT") != "orange=lemon":\n'
|
'if os.getenv("FRUIT") != "orange=lemon":\n'
|
||||||
' raise AssertionError')
|
' raise AssertionError')
|
||||||
args = [sys.executable, filename]
|
|
||||||
|
args = self.quote_args([program, filename])
|
||||||
newenv = os.environ.copy()
|
newenv = os.environ.copy()
|
||||||
newenv["FRUIT"] = "orange=lemon"
|
newenv["FRUIT"] = "orange=lemon"
|
||||||
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
|
exitcode = spawn(os.P_WAIT, program, args, newenv)
|
||||||
self.assertEqual(exitcode, 0)
|
self.assertEqual(exitcode, 0)
|
||||||
|
|
||||||
@requires_os_func('spawnve')
|
@requires_os_func('spawnve')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix the :func:`os.spawn* <os.spawnl>` tests failing on Windows
|
||||||
|
when the working directory or interpreter path contains spaces.
|
Loading…
Add table
Add a link
Reference in a new issue