Skip to content

Commit 4e6d935

Browse files
committed
Speed up unpack_file_url
E.g.: `pip install /path/to/dir` by building an sdist and then unpacking it instead of doing `shutil.copytree`. `shutil.copytree` may copy files that aren't included in the sdist, which means that: 1. If those files are large, a `pip install` could take much longer than it should. 2. Folks that are using `python setup.py install` to test their package are not fully testing it, because it may copy over files that wouldn't be there if they built and sdist and installed that. So this method building an sdist is both more accurate and faster. Fixes pypa#2195
1 parent 5a32a4d commit 4e6d935

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

pip/download.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
from pip.exceptions import InstallationError, HashMismatch
2323
from pip.models import PyPI
2424
from pip.utils import (splitext, rmtree, format_size, display_path,
25-
backup_dir, ask_path_exists, unpack_file)
25+
backup_dir, ask_path_exists, unpack_file,
26+
call_subprocess)
2627
from pip.utils.filesystem import check_path_owner
28+
from pip.utils.logging import indent_log
2729
from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner
2830
from pip.locations import write_delete_marker_file
2931
from pip.vcs import vcs
@@ -725,6 +727,45 @@ def unpack_file_url(link, location, download_dir=None):
725727
_copy_file(from_path, download_dir, content_type, link)
726728

727729

730+
def _copy_dist_from_dir(link_path, location):
731+
"""Copy distribution files in `link_path` to `location`.
732+
733+
Invoked when user requests to install a local directory. E.g.:
734+
735+
pip install .
736+
pip install ~/dev/git-repos/python-prompt-toolkit
737+
738+
"""
739+
740+
# Note: This is currently VERY SLOW if you have a lot of data in the
741+
# directory, because it copies everything with `shutil.copytree`.
742+
# What it should really do is build an sdist and install that.
743+
# See https://github.com/pypa/pip/issues/2195
744+
745+
if os.path.isdir(location):
746+
rmtree(location)
747+
748+
# build an sdist
749+
setup_py = 'setup.py'
750+
sdist_args = [sys.executable]
751+
sdist_args.append('-c')
752+
sdist_args.append(
753+
"import setuptools, tokenize;__file__=%r;"
754+
"exec(compile(getattr(tokenize, 'open', open)(__file__).read()"
755+
".replace('\\r\\n', '\\n'), __file__, 'exec'))" % setup_py)
756+
sdist_args.append('sdist')
757+
sdist_args += ['--dist-dir', location]
758+
logger.info('Running setup.py sdist for %s', link_path)
759+
760+
with indent_log():
761+
call_subprocess(sdist_args, cwd=link_path, show_stdout=False)
762+
763+
# unpack sdist into `location`
764+
sdist = os.path.join(location, os.listdir(location)[0])
765+
logger.info('Unpacking sdist %s into %s', sdist, location)
766+
unpack_file(sdist, location, content_type=None, link=None)
767+
768+
728769
class PipXmlrpcTransport(xmlrpc_client.Transport):
729770
"""Provide a `xmlrpclib.Transport` implementation via a `PipSession`
730771
object.

0 commit comments

Comments
 (0)