Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,30 @@ def test_special_chars_bash(self):
self.assertTrue(env_name.encode() in lines[0])
self.assertEndsWith(lines[1], env_name.encode())

# gh-160732: prompt values must not be evaluated when bash displays PS1.
@unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
@unittest.skipIf(sys.platform == 'cygwin', 'fail to locate cygpython DLL')
def test_prompt_command_substitution_bash(self):
"""Test that command substitutions in the prompt are not evaluated."""
rmtree(self.env_dir)
bash = shutil.which('bash')
if bash is None:
self.skipTest('bash required for this test')
marker = os.path.join(self.env_dir, 'prompt-command-ran')
prompt = f'$(touch {shlex.quote(marker)})'
builder = venv.EnvBuilder(clear=True, prompt=prompt)
builder.create(self.env_dir)
activate = os.path.join(self.env_dir, self.bindir, 'activate')
result = subprocess.run(
[bash, '--noprofile', '--norc', '-i'],
input=f'source {shlex.quote(activate)}\nexit\n',
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
self.assertEqual(result.returncode, 0)
self.assertFalse(os.path.exists(marker))

# gh-124651: test quoted strings
@unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
@unittest.skipIf(sys.platform.startswith('netbsd'),
Expand Down
19 changes: 18 additions & 1 deletion Lib/venv/scripts/common/activate
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ deactivate () {
unset _OLD_VIRTUAL_PS1
fi

if [ -n "${_OLD_VIRTUAL_PSVAR+x}" ] ; then
psvar[9]="${_OLD_VIRTUAL_PSVAR}"
unset _OLD_VIRTUAL_PSVAR
fi

unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
Expand Down Expand Up @@ -66,7 +71,19 @@ fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1="("__VENV_PROMPT__") ${PS1:-}"
if [ -n "${BASH_VERSION:-}" ] ; then
# Bash expands PS1 again when it displays the prompt. Keep the prompt
# in a variable so its contents are not interpreted as shell code.
PS1='(${VIRTUAL_ENV_PROMPT}) '"${PS1:-}"
elif [ -n "${ZSH_VERSION:-}" ] ; then
# zsh expands prompt substitutions in variable values. Use a psvar
# entry so the prompt is inserted as data instead.
_OLD_VIRTUAL_PSVAR="${psvar[9]-}"
psvar[9]="${VIRTUAL_ENV_PROMPT}"
PS1='(%9v) '"${PS1:-}"
else
PS1='(${VIRTUAL_ENV_PROMPT}) '"${PS1:-}"
fi
fi

# Call hash to forget past commands. Without forgetting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix command substitution in virtual environment prompts being evaluated by the
generated Bash activation script.
Loading