Repository: pypa/pip

Issue:
"pip install ." fails on Python 3.3 (with symlinks in local directory)
This seems to happen only if there are symlinks in the local directory. (In my case, the symlinks are from temporary build files.)

``` bash
$ pip install   .
Unpacking /Users/myint/projects/docformatter
Cleaning up...
Exception:
Traceback (most recent call last):
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/basecommand.py", line 134, in main
    status = self.run(options, args)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/commands/install.py", line 235, in run
    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/req.py", line 1084, in prepare_files
    self.unpack_url(url, location, self.is_download)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/req.py", line 1224, in unpack_url
    return unpack_file_url(link, loc)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/download.py", line 422, in unpack_file_url
    shutil.copytree(source, location)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/shutil.py", line 343, in copytree
    raise Error(errors)
shutil.Error: [('/Users/myint/projects/docformatter/.travis-solo/2.6/include/python2.6', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.6/include/python2.6', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.6/include/python2.6'"), ('/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/config', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.6/lib/python2.6/config', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/config'"), ('/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.6/lib/python2.6/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.6/lib/python2.6/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.6/lib/python2.6/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/2.7/include/python2.7', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.7/include/python2.7', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.7/include/python2.7'"), ('/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/config', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.7/lib/python2.7/config', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/config'"), ('/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.7/lib/python2.7/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/2.7/lib/python2.7/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/2.7/lib/python2.7/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/3.2/include/python3.2m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.2/include/python3.2m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.2/include/python3.2m'"), ('/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/config-3.2m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.2/lib/python3.2/config-3.2m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/config-3.2m'"), ('/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.2/lib/python3.2/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.2/lib/python3.2/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.2/lib/python3.2/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/include/python3.3m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/include/python3.3m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/include/python3.3m'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/collections', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/collections', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/collections'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/config-3.3m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/config-3.3m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/config-3.3m'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/importlib', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/importlib', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/importlib'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/plat-darwin', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/3.3/lib/python3.3/plat-darwin', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/3.3/lib/python3.3/plat-darwin'"), ('/Users/myint/projects/docformatter/.travis-solo/pypy/include', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/pypy/include', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/pypy/include'"), ('/Users/myint/projects/docformatter/.travis-solo/pypy/lib-python/2.7/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/pypy/lib-python/2.7/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/pypy/lib-python/2.7/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.6/include/python2.6', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.6/include/python2.6', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.6/include/python2.6'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/config', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.6/lib/python2.6/config', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/config'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.6/lib/python2.6/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.6/lib/python2.6/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.6/lib/python2.6/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.7/include/python2.7', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.7/include/python2.7', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.7/include/python2.7'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/config', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.7/lib/python2.7/config', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/config'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.7/lib/python2.7/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python2.7/lib/python2.7/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python2.7/lib/python2.7/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.2/include/python3.2m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.2/include/python3.2m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.2/include/python3.2m'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/config-3.2m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.2/lib/python3.2/config-3.2m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/config-3.2m'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.2/lib/python3.2/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.2/lib/python3.2/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.2/lib/python3.2/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/include/python3.3m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/include/python3.3m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/include/python3.3m'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/collections', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/collections', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/collections'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/config-3.3m', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/config-3.3m', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/config-3.3m'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/encodings', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/encodings', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/encodings'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/importlib', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/importlib', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/importlib'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/lib-dynload', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/lib-dynload', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/lib-dynload'"), ('/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/plat-darwin', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-5g3qy6-build/.travis-solo/python3.3/lib/python3.3/plat-darwin', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/.travis-solo/python3.3/lib/python3.3/plat-darwin'")]

Storing complete log in /Users/myint/.pip/pip.log
```

This seems to be due to `shutil.copytree()`, which fails if there are symlinks in the source.



Discussion:
can you explain the details of your symlink case, or provide a repeatable case.  thanks.

Yeah, it is easy to reproduce.

``` bash
$ git clone https://github.com/myint/docformatter
$ cd docformatter
$ ln -s /tmp
$ pip install .
Unpacking /Users/myint/projects/docformatter
Cleaning up...
Exception:
Traceback (most recent call last):
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/basecommand.py", line 134, in main
    status = self.run(options, args)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/commands/install.py", line 235, in run
    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/req.py", line 1084, in prepare_files
    self.unpack_url(url, location, self.is_download)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/req.py", line 1224, in unpack_url
    return unpack_file_url(link, loc)
  File "/Users/myint/Library/Python/3.3/lib/python/site-packages/pip/download.py", line 420, in unpack_file_url
    shutil.copytree(source, location)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/shutil.py", line 343, in copytree
    raise Error(errors)
shutil.Error: [('/Users/myint/projects/docformatter/tmp', '/var/folders/eD/eDkaCQuWHJG9f-G6kmkpgk+++TI/-Tmp-/pip-ayxv_5-build/tmp', "[Errno 21] Is a directory: '/Users/myint/projects/docformatter/tmp'")]
```

Note that the error goes away if I set `symlinks` parameter of `shutil.copytree()` to `True` in `pip/download.py`.

Any thoughts on my fix?

`copytree` in py2 doesn't trigger these errors.  did you discover the reason py3.3 behaves like this?

to be clear, the failure is occuring when copying from src to the build location, not when installing.
you're not intending to install a symlink are you?

FWIW Python 3.3 changed the way `rmtree()` worked, it's possible they also changed the way `copytree()` worked too.

Right, `copytree()` is just implemented differently in Python 3.

Also, correct, the symlink is not meant to be installed. The symlink is just some temporary file (as in the contents of `.tox` or a virtualenv, which include symlinks to various locations).


Resolving patch:
diff --git a/pip/download.py b/pip/download.py
--- a/pip/download.py
+++ b/pip/download.py
@@ -359,7 +359,7 @@ def unpack_file_url(link, location):
         # delete the location since shutil will create it again :(
         if os.path.isdir(location):
             rmtree(location)
-        shutil.copytree(source, location)
+        shutil.copytree(source, location, symlinks=True)
     else:
         unpack_file(source, location, content_type, link)
 


Test patch:
diff --git a/tests/data/packages/symlinks/doc/intro b/tests/data/packages/symlinks/doc/intro
new file mode 100644
diff --git a/tests/data/packages/symlinks/docs b/tests/data/packages/symlinks/docs
new file mode 120000
--- /dev/null
+++ b/tests/data/packages/symlinks/docs
@@ -0,0 +1 @@
+doc
\ No newline at end of file
diff --git a/tests/data/packages/symlinks/setup.cfg b/tests/data/packages/symlinks/setup.cfg
new file mode 100644
--- /dev/null
+++ b/tests/data/packages/symlinks/setup.cfg
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
diff --git a/tests/data/packages/symlinks/setup.py b/tests/data/packages/symlinks/setup.py
new file mode 100644
--- /dev/null
+++ b/tests/data/packages/symlinks/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+version = '0.1'
+
+setup(name='symlinks',
+      version=version,
+      packages=["symlinks"],
+      )
diff --git a/tests/data/packages/symlinks/symlinks/__init__.py b/tests/data/packages/symlinks/symlinks/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/data/packages/symlinks/symlinks/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -208,6 +208,18 @@ def test_install_from_local_directory(script, data):
     assert egg_info_folder in result.files_created, str(result)
 
 
+def test_install_from_local_directory_with_symlinks_to_directories(script, data):
+    """
+    Test installing from a local directory containing symlinks to directories.
+    """
+    to_install = data.packages.join("symlinks")
+    result = script.pip('install', to_install, expect_error=False)
+    pkg_folder = script.site_packages/'symlinks'
+    egg_info_folder = script.site_packages/'symlinks-0.1dev-py%s.egg-info' % pyversion
+    assert pkg_folder in result.files_created, str(result.stdout)
+    assert egg_info_folder in result.files_created, str(result)
+
+
 def test_install_from_local_directory_with_no_setup_py(script, data):
     """
     Test installing from a local directory with no 'setup.py'.
diff --git a/tests/lib/path.py b/tests/lib/path.py
--- a/tests/lib/path.py
+++ b/tests/lib/path.py
@@ -264,7 +264,7 @@ def copytree(self, to):
         """
         Copies a directory tree to another path.
         """
-        return shutil.copytree(self, to)
+        return shutil.copytree(self, to, symlinks=True)
 
     def move(self, to):
         """