Repository: ansible/ansible-modules-core

Issue:
pip uses pip2 and ignores virtualenv
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

pip
##### ANSIBLE VERSION

```
ansible 2.3.0 (devel 2804e64ed5) last updated 2016/10/21 09:59:53 (GMT +200)
  lib/ansible/modules/core: (detached HEAD 6f15dfd464) last updated 2016/10/18 16:01:36 (GMT +200)
  lib/ansible/modules/extras: (detached HEAD 3f77bb6857) last updated 2016/10/18 16:01:38 (GMT +200)
  config file = 
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

ø
##### OS / ENVIRONMENT

Debian 8.6 jessie on both machines.
##### SUMMARY

Using the ansible `pip` module with a virtualenv, ansible uses `/usr/bin/pip2` instead of the shiny pip3 contained in the virtualenv.
##### STEPS TO REPRODUCE

```
  - name: pip install requirements
    become: yes
    become_user: test
    pip:
      chdir: /home/test/
      requirements: requirements.txt
      virtualenv: /home/test/
      virtualenv_command: pyvenv
      virtualenv_python: python3
```

```
fatal: [dev]: FAILED! => {"changed": false, "cmd": "/usr/bin/pip2 install -r requirements.txt",
```
##### EXPECTED RESULTS

I expect the `ansible pip module` to use `python3 -m pip`, or at least a `pip3` found in the virtualenv (there is one).
##### ACTUAL RESULTS

Ansible uses `/usr/bin/pip2`.



Discussion:
<!-- @JulienPalard: Thanks for filing this bug report! -->

@mattupstate ping, this issue is waiting for your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_bug_new --->

It's a bug relates to the `_get_pip` function in `pip` module.

@JulienPalard take a look at #5359 

May fix my problem yes, but it may break others?

I'd be more confortable with a `python3 -m pip` as I'm explicitly giving `python3` as my virtualenv_python.

This also affects Ansible 2.2.0: the `pip` module uses `/usr/bin/pip2 list` instead of `bin/pip` or `bin/pip-2.7` from my virtualenv.


Resolving patch:
diff --git a/packaging/language/pip.py b/packaging/language/pip.py
--- a/packaging/language/pip.py
+++ b/packaging/language/pip.py
@@ -190,7 +190,7 @@
 import os
 import sys
 
-from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.basic import AnsibleModule, is_executable
 from ansible.module_utils._text import to_native
 from ansible.module_utils.six import PY3
 
@@ -263,17 +263,14 @@ def _is_present(name, version, installed_pkgs, pkg_command):
 
 
 def _get_pip(module, env=None, executable=None):
-    # On Debian and Ubuntu, pip is pip.
-    # On Fedora18 and up, pip is python-pip.
-    # On Fedora17 and below, CentOS and RedHat 6 and 5, pip is pip-python.
-    # On Fedora, CentOS, and RedHat, the exception is in the virtualenv.
-    # There, pip is just pip.
-    # On python 3.4, pip should be default, cf PEP 453
-    # By default, it will try to use pip required for the current python
+    # Older pip only installed under the "/usr/bin/pip" name.  Many Linux
+    # distros install it there.
+    # By default, we try to use pip required for the current python
     # interpreter, so people can use pip to install modules dependencies
-    candidate_pip_basenames = ['pip2', 'pip', 'python-pip', 'pip-python']
+    candidate_pip_basenames = ('pip2', 'pip')
     if PY3:
-        candidate_pip_basenames = ['pip3']
+        # pip under python3 installs the "/usr/bin/pip3" name
+        candidate_pip_basenames = ('pip3',)
 
     pip = None
     if executable is not None:
@@ -282,22 +279,39 @@ def _get_pip(module, env=None, executable=None):
             pip = executable
         else:
             # If you define your own executable that executable should be the only candidate.
-            candidate_pip_basenames = [executable]
+            # As noted in the docs, executable doesn't work with virtualenvs.
+            candidate_pip_basenames = (executable,)
+
     if pip is None:
         if env is None:
             opt_dirs = []
+            for basename in candidate_pip_basenames:
+                pip = module.get_bin_path(basename, False, opt_dirs)
+                if pip is not None:
+                    break
+            else:
+                # For-else: Means that we did not break out of the loop
+                # (therefore, that pip was not found)
+                module.fail_json(msg='Unable to find any of %s to use.  pip'
+                        ' needs to be installed.' % ', '.join(candidate_pip_basenames))
         else:
-            # Try pip with the virtualenv directory first.
-            opt_dirs = ['%s/bin' % env]
-        for basename in candidate_pip_basenames:
-            pip = module.get_bin_path(basename, False, opt_dirs)
-            if pip is not None:
-                break
-    # pip should have been found by now.  The final call to get_bin_path will
-    # trigger fail_json.
-    if pip is None:
-        basename = candidate_pip_basenames[0]
-        pip = module.get_bin_path(basename, True, opt_dirs)
+            # If we're using a virtualenv we must use the pip from the
+            # virtualenv
+            venv_dir = os.path.join(env, 'bin')
+            candidate_pip_basenames = (candidate_pip_basenames[0], 'pip')
+            for basename in candidate_pip_basenames:
+                candidate = os.path.join(venv_dir, basename)
+                if os.path.exists(candidate) and is_executable(candidate):
+                    pip = candidate
+                    break
+            else:
+                # For-else: Means that we did not break out of the loop
+                # (therefore, that pip was not found)
+                module.fail_json(msg='Unable to find pip in the virtualenv,'
+                        ' %s, under any of these names: %s. Make sure pip is'
+                        ' present in the virtualenv.' % (env,
+                            ', '.join(candidate_pip_basenames)))
+
     return pip
 
 


Test patch: