8000 gh-75729: Fix os.spawn tests not handling spaces on Windows (#99150) · python/cpython@a34c796 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a34c796

Browse files
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>
1 parent 1377496 commit a34c796

File tree

2 files changed

+59
-45
lines changed

2 files changed

+59
-45
lines changed

Lib/test/test_os.py

Lines changed: 57 additions & 45 deletions
B41A
Original file line numberDiff line numberDiff line change
@@ -3214,6 +3214,14 @@ def kill_process(pid):
32143214

32153215
@support.requires_subprocess()
32163216
class SpawnTests(unittest.TestCase):
3217+
@staticmethod
3218+
def quote_args(args):
3219+
# On Windows, os.spawn* simply joins arguments with spaces:
3220+
# arguments need to be quoted
3221+
if os.name != 'nt':
3222+
return args
3223+
return [f'"{arg}"' if " " in arg.strip() else arg for arg in args]
3224+
32173225
def create_args(self, *, with_env=False, use_bytes=False):
32183226
self.exitcode = 17
32193227

@@ -3234,115 +3242,118 @@ def create_args(self, *, with_env=False, use_bytes=False):
32343242
with open(filename, "w", encoding="utf-8") as fp:
32353243
fp.write(code)
32363244

3237-
args = [sys.executable, filename]
3245+
program = sys.executable
3246+
args = self.quote_args([program, filename])
32383247
if use_bytes:
3248+
program = os.fsencode(program)
32393249
args = [os.fsencode(a) for a in args]
32403250
self.env = {os.fsencode(k): os.fsencode(v)
32413251
for k, v in self.env.items()}
32423252

3243-
return args
3253+
return program, args
32443254

32453255
@requires_os_func('spawnl')
32463256
def test_spawnl(self):
3247-
args = self.create_args()
3248-
exitcode = os.spawnl(os.P_WAIT, args[0], *args)
3257+
program, args = self.create_args()
3258+
exitcode = os.spawnl(os.P_WAIT, program, *args)
32493259
self.assertEqual(exitcode, self.exitcode)
32503260

32513261
@requires_os_func('spawnle')
32523262
def test_spawnle(self):
3253-
args = self.create_args(with_env=True)
3254-
exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env)
3263+
program, args = self.create_args(with_env=True)
3264+
exitcode = os.spawnle(os.P_WAIT, program, *args, self.env)
32553265
self.assertEqual(exitcode, self.exitcode)
32563266

32573267
@requires_os_func('spawnlp')
32583268
def test_spawnlp(self):
3259-
args = self.create_args()
3260-
exitcode = os.spawnlp(os.P_WAIT, args[0], *args)
3269+
program, args = self.create_args()
3270+
exitcode = os.spawnlp(os.P_WAIT, program, *args)
32613271
self.assertEqual(exitcode, self.exitcode)
32623272

32633273
@requires_os_func('spawnlpe')
32643274
def test_spawnlpe(self):
3265-
args = self.create_args(with_env=True)
3266-
exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env)
3275+
program, args = self.create_args(with_env=True)
3276+
exitcode = os.spawnlpe(os.P_WAIT, program, *args, self.env)
32673277
self.assertEqual(exitcode, self.exitcode)
32683278

32693279
@requires_os_func('spawnv')
32703280
def test_spawnv(self):
3271-
args = self.create_args()
3272-
exitcode = os.spawnv(os.P_WAIT, args[0], args)
3281+
program, args = self.create_args()
3282+
exitcode = os.spawnv(os.P_WAIT, program, args)
32733283
self.assertEqual(exitcode, self.exitcode)
32743284

32753285
# Test for PyUnicode_FSConverter()
3276-
exitcode = os.spawnv(os.P_WAIT, FakePath(args[0]), args)
3286+
exitcode = os.spawnv(os.P_WAIT, FakePath(program), args)
32773287
self.assertEqual(exitcode, self.exitcode)
32783288

32793289
@requires_os_func('spawnve')
32803290
def test_spawnve(self):
3281-
args = self.create_args(with_env=True)
3282-
exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env)
3291+
program, args = self.create_args(with_env=True)
3292+
exitcode = os.spawnve(os.P_WAIT, program, args, self.env)
32833293
self.assertEqual(exitcode, self.exitcode)
32843294

32853295
@requires_os_func('spawnvp')
32863296
def test_spawnvp(self):
3287-
args = self.create_args()
3288-
exitcode = os.spawnvp(os.P_WAIT, args[0], args)
3297+
program, args = self.create_args()
3298+
exitcode = os.spawnvp(os.P_WAIT, program, args)
32893299
self.assertEqual(exitcode, self.exitcode)
32903300

32913301
@requires_os_func('spawnvpe')
32923302
def test_spawnvpe(self):
3293-
args = self.create_args(with_env=True)
3294-
exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env)
3303+
program, args = self.create_args(with_env=True)
3304+
exitcode = os.spawnvpe(os.P_WAIT, program, args, self.env)
32953305
self.assertEqual(exitcode, self.exitcode)
32963306

32973307
@requires_os_func('spawnv')
32983308
def test_nowait(self):
3299-
args = self.create_args()
3300-
pid = os.spawnv(os.P_NOWAIT, args[0], args)
3309+
program, args = self.create_args()
3310+
pid = os.spawnv(os.P_NOWAIT, program, args)
33013311
support.wait_process(pid, exitcode=self.exitcode)
33023312

33033313
@requires_os_func('spawnve')
33043314
def test_spawnve_bytes(self):
33053315
# Test bytes handling in parse_arglist and parse_envlist (#28114)
3306-
args = self.create_args(with_env=True, use_bytes=True)
3307-
exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env)
3316+
program, args = self.create_args(with_env=True, use_bytes=True)
3317+
exitcode = os.spawnve(os.P_WAIT, program, args, self.env)
33083318
self.assertEqual(exitcode, self.exitcode)
33093319

33103320
@requires_os_func('spawnl')
33113321
def test_spawnl_noargs(self):
3312-
args = self.create_args()
3313-
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0])
3314-
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0], '')
3322+
program, __ = self.create_args()
3323+
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program)
3324+
self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program, '')
33153325

33163326
@requires_os_func('spawnle')
33173327
def test_spawnle_noargs(self):
3318-
args = self.create_args()
3319-
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], {})
3320-
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], '', {})
3328+
program, __ = self.create_args()
3329+
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, {})
3330+
self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, '', {})
33213331

33223332
@requires_os_func('spawnv')
33233333
def test_spawnv_noargs(self):
3324-
args = self.create_args()
3325-
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ())
3326-
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], [])
3327-
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ('',))
3328-
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], [''])
3334+
program, __ = self.create_args()
3335+
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ())
3336+
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, [])
3337+
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ('',))
3338+
self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, [''])
33293339

33303340
@requires_os_func('spawnve')
33313341
def test_spawnve_noargs(self):
3332-
args = self.create_args()
3333-
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], (), {})
3334-
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [], {})
3335-
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], ('',), {})
3336-
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [''], {})
3342+
program, __ = self.create_args()
3343+
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, (), {})
3344+
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, [], {})
3345+
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, ('',), {})
3346+
self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, program, [''], {})
33373347

33383348
def _test_invalid_env(self, spawn):
3339-
args = [sys.executable, '-c', 'pass']
3349+
program = sys.executable
3350+
args = self.quote_args([program, '-c', 'pass'])
33403351

33413352
# null character in the environment variable name
33423353
newenv = os.environ.copy()
33433354
newenv["FRUIT\0VEGETABLE"] = "cabbage"
33443355
try:
3345-
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
3356+
exitcode = spawn(os.P_WAIT, program, args, newenv)
33463357
except ValueError:
33473358
pass
33483359
else:
@@ -3352,7 +3363,7 @@ def _test_invalid_env(self, spawn):
33523363
newenv = os.environ.copy()
33533364
newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
33543365
try:
3355-
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
3366+
exitcode = spawn(os.P_WAIT, program, args, newenv)
33563367
except ValueError:
33573368
pass
33583369
else:
@@ -3362,7 +3373,7 @@ def _test_invalid_env(self, spawn):
33623373
newenv = os.environ.copy()
33633374
newenv["FRUIT=ORANGE"] = "lemon"
33643375
try:
3365-
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
3376+
exitcode = spawn(os.P_WAIT, program, args, newenv)
33663377
except ValueError:
33673378
pass
33683379
else:
@@ -3375,10 +3386,11 @@ def _test_invalid_env(self, spawn):
33753386
fp.write('import sys, os\n'
33763387
'if os.getenv("FRUIT") != "orange=lemon":\n'
33773388
' raise AssertionError')
3378-
args = [sys.executable, filename]
3389+
3390+
args = self.quote_args([program, filename])
33793391
newenv = os.environ.copy()
33803392
newenv["FRUIT"] = "orange=lemon"
3381-
exitcode = spawn(os.P_WAIT, args[0], args, newenv)
3393+
exitcode = spawn(os.P_WAIT, program, args, newenv)
33823394
self.assertEqual(exitcode, 0)
33833395

33843396
@requires_os_func('spawnve')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the :func:`os.spawn* <os.spawnl>` tests failing on Windows
2+
when the working directory or interpreter path contains spaces.

0 commit comments

Comments
 (0)
0