diff --git a/changelogs/fragments/67407-pip-virtualenv_command-args.yml b/changelogs/fragments/67407-pip-virtualenv_command-args.yml new file mode 100644 index 00000000000..b072a17f487 --- /dev/null +++ b/changelogs/fragments/67407-pip-virtualenv_command-args.yml @@ -0,0 +1,4 @@ +bugfixes: + - pip - The virtualenv_command option can now include arguments without + requiring the full path to the binary. + (https://github.com/ansible/ansible/issues/52275) diff --git a/lib/ansible/modules/packaging/language/pip.py b/lib/ansible/modules/packaging/language/pip.py index 19744623d0c..9ce74e6b09d 100644 --- a/lib/ansible/modules/packaging/language/pip.py +++ b/lib/ansible/modules/packaging/language/pip.py @@ -471,23 +471,30 @@ def setup_virtualenv(module, env, chdir, out, err): if module.check_mode: module.exit_json(changed=True) - cmd = module.params['virtualenv_command'] - if os.path.basename(cmd) == cmd: - cmd = module.get_bin_path(cmd, True) + cmd = shlex.split(module.params['virtualenv_command']) + # Find the binary for the command in the PATH + # and switch the command for the explicit path. + if os.path.basename(cmd[0]) == cmd[0]: + cmd[0] = module.get_bin_path(cmd[0], True) + + # Add the system-site-packages option if that + # is enabled, otherwise explicitly set the option + # to not use system-site-packages if that is an + # option provided by the command's help function. if module.params['virtualenv_site_packages']: - cmd += ' --system-site-packages' + cmd.append('--system-site-packages') else: - cmd_opts = _get_cmd_options(module, cmd) + cmd_opts = _get_cmd_options(module, cmd[0]) if '--no-site-packages' in cmd_opts: - cmd += ' --no-site-packages' + cmd.append('--no-site-packages') virtualenv_python = module.params['virtualenv_python'] # -p is a virtualenv option, not compatible with pyenv or venv - # this if validates if the command being used is not any of them + # this conditional validates if the command being used is not any of them if not any(ex in module.params['virtualenv_command'] for ex in ('pyvenv', '-m venv')): if virtualenv_python: - cmd += ' -p%s' % virtualenv_python + cmd.append('-p%s' % virtualenv_python) elif PY3: # Ubuntu currently has a patch making virtualenv always # try to use python2. Since Ubuntu16 works without @@ -495,7 +502,7 @@ def setup_virtualenv(module, env, chdir, out, err): # the upstream behaviour of using the python which invoked # virtualenv to determine which python is used inside of # the virtualenv (when none are specified). - cmd += ' -p%s' % sys.executable + cmd.append('-p%s' % sys.executable) # if venv or pyvenv are used and virtualenv_python is defined, then # virtualenv_python is ignored, this has to be acknowledged @@ -505,7 +512,7 @@ def setup_virtualenv(module, env, chdir, out, err): ' using the venv module or pyvenv as virtualenv_command' ) - cmd = "%s %s" % (cmd, env) + cmd.append(env) rc, out_venv, err_venv = module.run_command(cmd, cwd=chdir) out += out_venv err += err_venv diff --git a/test/integration/targets/pip/tasks/pip.yml b/test/integration/targets/pip/tasks/pip.yml index c119f5726e2..477d4e601b0 100644 --- a/test/integration/targets/pip/tasks/pip.yml +++ b/test/integration/targets/pip/tasks/pip.yml @@ -517,6 +517,32 @@ that: "'distribute' in remove_distribute.cmd" when: ansible_python.version.major == 2 +### test virtualenv_command begin ### + +- name: Test virtualenv command with arguments + when: "ansible_system == 'Linux'" + block: + - name: make sure the virtualenv does not exist + file: + state: absent + name: "{{ output_dir }}/pipenv" + + # ref: https://github.com/ansible/ansible/issues/52275 + - name: install using virtualenv_command with arguments + pip: + name: "{{ pip_test_package }}" + virtualenv: "{{ output_dir }}/pipenv" + virtualenv_command: "virtualenv --verbose" + state: present + register: version13 + + - name: ensure install using virtualenv_command with arguments was successful + assert: + that: + - "version13 is success" + +### test virtualenv_command end ### + # https://github.com/ansible/ansible/issues/68592 # Handle pre-release version numbers in check_mode for already-installed # packages.