diff --git a/src/pip/_internal/req/__init__.py b/src/pip/_internal/req/__init__.py index 75b532b0e..8568d3f8b 100644 --- a/src/pip/_internal/req/__init__.py +++ b/src/pip/_internal/req/__init__.py @@ -1,8 +1,6 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - from __future__ import absolute_import +import collections import logging from pip._internal.utils.logging import indent_log @@ -13,7 +11,7 @@ from .req_install import InstallRequirement from .req_set import RequirementSet if MYPY_CHECK_RUNNING: - from typing import List, Optional, Sequence + from typing import Iterator, List, Optional, Sequence, Tuple __all__ = [ "RequirementSet", "InstallRequirement", @@ -33,8 +31,17 @@ class InstallationResult(object): return "InstallationResult(name={!r})".format(self.name) +def _validate_requirements( + requirements, # type: List[InstallRequirement] +): + # type: (...) -> Iterator[Tuple[str, InstallRequirement]] + for req in requirements: + assert req.name, "invalid to-be-installed requirement: {}".format(req) + yield req.name, req + + def install_given_reqs( - to_install, # type: List[InstallRequirement] + requirements, # type: List[InstallRequirement] install_options, # type: List[str] global_options, # type: Sequence[str] root, # type: Optional[str] @@ -50,23 +57,27 @@ def install_given_reqs( (to be called after having downloaded and unpacked the packages) """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) if to_install: logger.info( 'Installing collected packages: %s', - ', '.join([req.name for req in to_install]), + ', '.join(to_install.keys()), ) installed = [] with indent_log(): - for requirement in to_install: + for req_name, requirement in to_install.items(): if requirement.should_reinstall: - logger.info('Attempting uninstall: %s', requirement.name) + logger.info('Attempting uninstall: %s', req_name) with indent_log(): uninstalled_pathset = requirement.uninstall( auto_confirm=True ) + else: + uninstalled_pathset = None + try: requirement.install( install_options, @@ -79,22 +90,14 @@ def install_given_reqs( pycompile=pycompile, ) except Exception: - should_rollback = ( - requirement.should_reinstall and - not requirement.install_succeeded - ) # if install did not succeed, rollback previous uninstall - if should_rollback: + if uninstalled_pathset and not requirement.install_succeeded: uninstalled_pathset.rollback() raise else: - should_commit = ( - requirement.should_reinstall and - requirement.install_succeeded - ) - if should_commit: + if uninstalled_pathset and requirement.install_succeeded: uninstalled_pathset.commit() - installed.append(InstallationResult(requirement.name)) + installed.append(InstallationResult(req_name)) return installed