Repository: pypa/pip

Issue:
pip install --target ignores platlib directories
- Pip version: 8.1.2
- Python version: 2.7.5
- Operating System: centos7 64 bit
### Description:

pip install with --target option has an issue with lib64 packages(platlib).

1) When I tried to install mysql-python using the following command, I ran into an error saying no such file or directory.

`pip install --target=/var/tmp/ mysql-python`

Issue:
The issue is that the package is installed in platlib directory " /tmp/{random_string}/lib64/python " but pip is trying to move files only from purelib directory to target directory but not platlib directory. So it checks for directories under "/tmp/{random_string}/lib/python" and throws exception saying no such file or directory.

2) When I tried to install paramiko==1.16.0 using the following command, I found pycrypto package is missing which is a dependent package for paramiko.

`pip install --target=/var/tmp/paramiko paramiko==1.16.0``

Issue:
There are three packages (paramiko, ecdsa, pycrypto) that are installed in /tmp/{random_string} directory. paramiko and ecdsa are installed in purelib "/tmp/{random_string}/lib/python" and pycrypto is installed in platlib "/tmp/{random_string}/lib64/python" which is expected based on system architecture. But pip moves files under purelib to target directory but not files under platlib, as a result pycrypto packages in missing. Moreover the success message says "Successfully installed ecdsa-0.13 paramiko-1.16.0 pycrypto-2.6.1"
### What I've run:

`pip install --target=/var/tmp/ mysql-python`

``` Exception:
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/pip/basecommand.py", line 215, in main
    status = self.run(options, args)
  File "/usr/lib/python2.7/site-packages/pip/commands/install.py", line 368, in run
    for item in os.listdir(lib_dir):
OSError: [Errno 2] No such file or directory: '/tmp/tmpbtxE72/lib/python'
```

```
pip install --target=/var/tmp/paramiko paramiko==1.16.0
```

```
Collecting paramiko==1.16.0
  Using cached paramiko-1.16.0-py2.py3-none-any.whl
Collecting ecdsa>=0.11 (from paramiko==1.16.0)
  Using cached ecdsa-0.13-py2.py3-none-any.whl
Collecting pycrypto!=2.4,>=2.1 (from paramiko==1.16.0)
  Using cached pycrypto-2.6.1.tar.gz
Installing collected packages: ecdsa, pycrypto, paramiko
  Running setup.py install for pycrypto ... done
Successfully installed ecdsa-0.13 paramiko-1.16.0 pycrypto-2.6.1
```
### Note:

I'm planning to fix this and raise a pull request so that pip considers both purelib as well as platlib (if both are different and exists)



Discussion:

Resolving patch:
diff --git a/pip/commands/install.py b/pip/commands/install.py
--- a/pip/commands/install.py
+++ b/pip/commands/install.py
@@ -352,35 +352,46 @@ def run(self, options, args):
         if options.target_dir:
             ensure_dir(options.target_dir)
 
-            lib_dir = distutils_scheme('', home=temp_target_dir)['purelib']
-
-            for item in os.listdir(lib_dir):
-                target_item_dir = os.path.join(options.target_dir, item)
-                if os.path.exists(target_item_dir):
-                    if not options.upgrade:
-                        logger.warning(
-                            'Target directory %s already exists. Specify '
-                            '--upgrade to force replacement.',
-                            target_item_dir
-                        )
-                        continue
-                    if os.path.islink(target_item_dir):
-                        logger.warning(
-                            'Target directory %s already exists and is '
-                            'a link. Pip will not automatically replace '
-                            'links, please remove if replacement is '
-                            'desired.',
-                            target_item_dir
-                        )
-                        continue
-                    if os.path.isdir(target_item_dir):
-                        shutil.rmtree(target_item_dir)
-                    else:
-                        os.remove(target_item_dir)
-
-                shutil.move(
-                    os.path.join(lib_dir, item),
-                    target_item_dir
-                )
+            # Checking both purelib and platlib directories for installed
+            # packages to be moved to target directory
+            lib_dir_list = []
+
+            purelib_dir = distutils_scheme('', home=temp_target_dir)['purelib']
+            platlib_dir = distutils_scheme('', home=temp_target_dir)['platlib']
+
+            if os.path.exists(purelib_dir):
+                lib_dir_list.append(purelib_dir)
+            if os.path.exists(platlib_dir) and platlib_dir != purelib_dir:
+                lib_dir_list.append(platlib_dir)
+
+            for lib_dir in lib_dir_list:
+                for item in os.listdir(lib_dir):
+                    target_item_dir = os.path.join(options.target_dir, item)
+                    if os.path.exists(target_item_dir):
+                        if not options.upgrade:
+                            logger.warning(
+                                'Target directory %s already exists. Specify '
+                                '--upgrade to force replacement.',
+                                target_item_dir
+                            )
+                            continue
+                        if os.path.islink(target_item_dir):
+                            logger.warning(
+                                'Target directory %s already exists and is '
+                                'a link. Pip will not automatically replace '
+                                'links, please remove if replacement is '
+                                'desired.',
+                                target_item_dir
+                            )
+                            continue
+                        if os.path.isdir(target_item_dir):
+                            shutil.rmtree(target_item_dir)
+                        else:
+                            os.remove(target_item_dir)
+
+                    shutil.move(
+                        os.path.join(lib_dir, item),
+                        target_item_dir
+                    )
             shutil.rmtree(temp_target_dir)
         return requirement_set


Test patch: