Skip to content

Commit c2b4d5f

Browse files
committed
gh-148315: Update test_venv for shell-quoted command line
1 parent 93b77cd commit c2b4d5f

File tree

1 file changed

+33
-4
lines changed

1 file changed

+33
-4
lines changed

Lib/test/test_venv.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,12 @@ def _check_output_of_default_create(self):
146146
self.assertIn('home = %s' % path, data)
147147
self.assertIn('executable = %s' %
148148
os.path.realpath(sys.executable), data)
149-
copies = '' if os.name=='nt' else ' --copies'
150-
cmd = (f'command = {sys.executable} -m venv{copies} --without-pip '
151-
f'--without-scm-ignore-files {self.env_dir}')
149+
expected_argv = [sys.executable, '-m', 'venv']
150+
if os.name != 'nt':
151+
expected_argv.append('--copies')
152+
expected_argv.extend(['--without-pip', '--without-scm-ignore-files',
153+
self.env_dir])
154+
cmd = f'command = {shlex.join(expected_argv)}'
152155
self.assertIn(cmd, data)
153156
fn = self.get_env_file(self.bindir, self.exe)
154157
if not os.path.exists(fn): # diagnostics for Windows buildbot failures
@@ -166,7 +169,7 @@ def test_config_file_command_key(self):
166169
('--clear', 'clear', True),
167170
('--upgrade', 'upgrade', True),
168171
('--upgrade-deps', 'upgrade_deps', True),
169-
('--prompt="foobar"', 'prompt', 'foobar'),
172+
('--prompt', 'prompt', 'foobar'),
170173
('--without-scm-ignore-files', 'scm_ignore_files', frozenset()),
171174
]
172175
for opt, attr, value in options:
@@ -190,6 +193,32 @@ def test_config_file_command_key(self):
190193
else:
191194
self.assertRegex(data, rf'command = .* {opt}')
192195

196+
def test_config_file_command_quotes_paths_with_spaces(self):
197+
# gh-148315: the `command = ...` line written to pyvenv.cfg must be
198+
# shell-quoted, so a venv created in a directory with whitespace in
199+
# its path (as happens on Windows when the user directory contains a
200+
# space, e.g. "C:\\Users\\Z B") round-trips through shlex.split as
201+
# a single token instead of being truncated at the space.
202+
env_dir_with_space = os.path.join(tempfile.mkdtemp(), 'with space')
203+
self.addCleanup(rmtree, os.path.dirname(env_dir_with_space))
204+
b = venv.EnvBuilder()
205+
b.upgrade_dependencies = Mock()
206+
b._setup_pip = Mock()
207+
self.run_with_capture(b.create, env_dir_with_space)
208+
cfg = pathlib.Path(env_dir_with_space, 'pyvenv.cfg').read_text(
209+
encoding='utf-8')
210+
for line in cfg.splitlines():
211+
key, _, value = line.partition('=')
212+
if key.strip() == 'command':
213+
parts = shlex.split(value.strip())
214+
break
215+
else:
216+
self.fail(f'pyvenv.cfg is missing a command key:\n{cfg}')
217+
# Last token must be the full env_dir, not a space-split fragment.
218+
self.assertEqual(parts[-1], env_dir_with_space)
219+
# And the whole argv must be parseable by the venv CLI.
220+
self.assertEqual(parts[1:3], ['-m', 'venv'])
221+
193222
def test_prompt(self):
194223
env_name = os.path.split(self.env_dir)[1]
195224

0 commit comments

Comments
 (0)