diff --git a/docs/html/reference/pip_install.rst b/docs/html/reference/pip_install.rst index 96fd1c018ba..f56dc9a0e9f 100644 --- a/docs/html/reference/pip_install.rst +++ b/docs/html/reference/pip_install.rst @@ -694,10 +694,21 @@ does not satisfy the ``--require-hashes`` demand that every package have a local hash. +Local project installs +++++++++++++++++++++++ +pip supports installing local project in both regular mode and editable mode. +You can install local projects by specifying the project path to pip:: + +$ pip install path/to/SomeProject + +During regular installation, pip will copy the entire project directory to a temporary location and install from there. +The exception is that pip will exclude .tox and .nox directories present in the top level of the project from being copied. + + .. _`editable-installs`: "Editable" Installs -+++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~ "Editable" installs are fundamentally `"setuptools develop mode" `_ diff --git a/news/6770.bugfix b/news/6770.bugfix new file mode 100644 index 00000000000..c0ab57ee109 --- /dev/null +++ b/news/6770.bugfix @@ -0,0 +1 @@ +Skip copying .tox and .nox directories to temporary build directories diff --git a/src/pip/_internal/download.py b/src/pip/_internal/download.py index 8715eb5b19d..95963a1d2d5 100644 --- a/src/pip/_internal/download.py +++ b/src/pip/_internal/download.py @@ -934,12 +934,23 @@ def unpack_file_url( of the link file inside download_dir. """ link_path = url_to_path(link.url_without_fragment) - # If it's a url to a local directory if is_dir_url(link): + + def ignore(d, names): + # Pulling in those directories can potentially be very slow, + # exclude the following directories if they appear in the top + # level dir (and only it). + # See discussion at https://github.com/pypa/pip/pull/6770 + return ['.tox', '.nox'] if d == link_path else [] + if os.path.isdir(location): rmtree(location) - shutil.copytree(link_path, location, symlinks=True) + shutil.copytree(link_path, + location, + symlinks=True, + ignore=ignore) + if download_dir: logger.info('Link is a directory, ignoring download_dir') return diff --git a/tests/unit/test_download.py b/tests/unit/test_download.py index 7b421a7d7d5..afc32e29dca 100644 --- a/tests/unit/test_download.py +++ b/tests/unit/test_download.py @@ -413,6 +413,41 @@ def test_unpack_file_url_thats_a_dir(self, tmpdir, data): assert os.path.isdir(os.path.join(self.build_dir, 'fspkg')) +@pytest.mark.parametrize('exclude_dir', [ + '.nox', + '.tox' +]) +def test_unpack_file_url_excludes_expected_dirs(tmpdir, exclude_dir): + src_dir = tmpdir / 'src' + dst_dir = tmpdir / 'dst' + src_included_file = src_dir.joinpath('file.txt') + src_excluded_dir = src_dir.joinpath(exclude_dir) + src_excluded_file = src_dir.joinpath(exclude_dir, 'file.txt') + src_included_dir = src_dir.joinpath('subdir', exclude_dir) + + # set up source directory + src_excluded_dir.mkdir(parents=True) + src_included_dir.mkdir(parents=True) + src_included_file.touch() + src_excluded_file.touch() + + dst_included_file = dst_dir.joinpath('file.txt') + dst_excluded_dir = dst_dir.joinpath(exclude_dir) + dst_excluded_file = dst_dir.joinpath(exclude_dir, 'file.txt') + dst_included_dir = dst_dir.joinpath('subdir', exclude_dir) + + src_link = Link(path_to_url(src_dir)) + unpack_file_url( + src_link, + dst_dir, + download_dir=None + ) + assert not os.path.isdir(dst_excluded_dir) + assert not os.path.isfile(dst_excluded_file) + assert os.path.isfile(dst_included_file) + assert os.path.isdir(dst_included_dir) + + class TestSafeFileCache: """ The no_perms test are useless on Windows since SafeFileCache uses