mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
0cabefbce8
The legacy resolver seems to prepare requirements differently that can leave them half-done in certain code paths. I'm not quite sure how it's doing things, but at least we can do this to make sure they do things right.
148 lines
5.2 KiB
Python
148 lines
5.2 KiB
Python
import logging
|
|
import os
|
|
from optparse import Values
|
|
from typing import List
|
|
|
|
from pip._internal.cli import cmdoptions
|
|
from pip._internal.cli.cmdoptions import make_target_python
|
|
from pip._internal.cli.req_command import RequirementCommand, with_cleanup
|
|
from pip._internal.cli.status_codes import SUCCESS
|
|
from pip._internal.operations.build.build_tracker import get_build_tracker
|
|
from pip._internal.req.req_install import check_legacy_setup_py_options
|
|
from pip._internal.utils.misc import ensure_dir, normalize_path, write_output
|
|
from pip._internal.utils.temp_dir import TempDirectory
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class DownloadCommand(RequirementCommand):
|
|
"""
|
|
Download packages from:
|
|
|
|
- PyPI (and other indexes) using requirement specifiers.
|
|
- VCS project urls.
|
|
- Local project directories.
|
|
- Local or remote source archives.
|
|
|
|
pip also supports downloading from "requirements files", which provide
|
|
an easy way to specify a whole environment to be downloaded.
|
|
"""
|
|
|
|
usage = """
|
|
%prog [options] <requirement specifier> [package-index-options] ...
|
|
%prog [options] -r <requirements file> [package-index-options] ...
|
|
%prog [options] <vcs project url> ...
|
|
%prog [options] <local project path> ...
|
|
%prog [options] <archive url/path> ..."""
|
|
|
|
def add_options(self) -> None:
|
|
self.cmd_opts.add_option(cmdoptions.constraints())
|
|
self.cmd_opts.add_option(cmdoptions.requirements())
|
|
self.cmd_opts.add_option(cmdoptions.no_deps())
|
|
self.cmd_opts.add_option(cmdoptions.global_options())
|
|
self.cmd_opts.add_option(cmdoptions.no_binary())
|
|
self.cmd_opts.add_option(cmdoptions.only_binary())
|
|
self.cmd_opts.add_option(cmdoptions.prefer_binary())
|
|
self.cmd_opts.add_option(cmdoptions.src())
|
|
self.cmd_opts.add_option(cmdoptions.pre())
|
|
self.cmd_opts.add_option(cmdoptions.require_hashes())
|
|
self.cmd_opts.add_option(cmdoptions.progress_bar())
|
|
self.cmd_opts.add_option(cmdoptions.no_build_isolation())
|
|
self.cmd_opts.add_option(cmdoptions.use_pep517())
|
|
self.cmd_opts.add_option(cmdoptions.no_use_pep517())
|
|
self.cmd_opts.add_option(cmdoptions.check_build_deps())
|
|
self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
|
|
|
|
self.cmd_opts.add_option(
|
|
"-d",
|
|
"--dest",
|
|
"--destination-dir",
|
|
"--destination-directory",
|
|
dest="download_dir",
|
|
metavar="dir",
|
|
default=os.curdir,
|
|
help="Download packages into <dir>.",
|
|
)
|
|
|
|
cmdoptions.add_target_python_options(self.cmd_opts)
|
|
|
|
index_opts = cmdoptions.make_option_group(
|
|
cmdoptions.index_group,
|
|
self.parser,
|
|
)
|
|
|
|
self.parser.insert_option_group(0, index_opts)
|
|
self.parser.insert_option_group(0, self.cmd_opts)
|
|
|
|
@with_cleanup
|
|
def run(self, options: Values, args: List[str]) -> int:
|
|
options.ignore_installed = True
|
|
# editable doesn't really make sense for `pip download`, but the bowels
|
|
# of the RequirementSet code require that property.
|
|
options.editables = []
|
|
|
|
cmdoptions.check_dist_restriction(options)
|
|
|
|
options.download_dir = normalize_path(options.download_dir)
|
|
ensure_dir(options.download_dir)
|
|
|
|
session = self.get_default_session(options)
|
|
|
|
target_python = make_target_python(options)
|
|
finder = self._build_package_finder(
|
|
options=options,
|
|
session=session,
|
|
target_python=target_python,
|
|
ignore_requires_python=options.ignore_requires_python,
|
|
)
|
|
|
|
build_tracker = self.enter_context(get_build_tracker())
|
|
|
|
directory = TempDirectory(
|
|
delete=not options.no_clean,
|
|
kind="download",
|
|
globally_managed=True,
|
|
)
|
|
|
|
reqs = self.get_requirements(args, options, finder, session)
|
|
check_legacy_setup_py_options(options, reqs)
|
|
|
|
preparer = self.make_requirement_preparer(
|
|
temp_build_dir=directory,
|
|
options=options,
|
|
build_tracker=build_tracker,
|
|
session=session,
|
|
finder=finder,
|
|
download_dir=options.download_dir,
|
|
use_user_site=False,
|
|
verbosity=self.verbosity,
|
|
)
|
|
|
|
resolver = self.make_resolver(
|
|
preparer=preparer,
|
|
finder=finder,
|
|
options=options,
|
|
ignore_requires_python=options.ignore_requires_python,
|
|
use_pep517=options.use_pep517,
|
|
py_version_info=options.python_version,
|
|
)
|
|
|
|
self.trace_basic_info(finder)
|
|
|
|
requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
|
|
|
|
downloaded: List[str] = []
|
|
for req in requirement_set.requirements.values():
|
|
if req.satisfied_by is None:
|
|
assert req.name is not None
|
|
preparer.save_linked_requirement(req)
|
|
downloaded.append(req.name)
|
|
|
|
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
|
|
requirement_set.warn_legacy_versions_and_specifiers()
|
|
|
|
if downloaded:
|
|
write_output("Successfully downloaded %s", " ".join(downloaded))
|
|
|
|
return SUCCESS
|