diff --git a/news/12791.bugfix.rst b/news/12791.bugfix.rst new file mode 100644 index 00000000000..c19345d439c --- /dev/null +++ b/news/12791.bugfix.rst @@ -0,0 +1,2 @@ +Improve pip install performance. The installed packages printout is +now calculated in linear time instead of quadratic time. diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index d5b06c8c785..5e076a2d1e2 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -7,6 +7,7 @@ from optparse import SUPPRESS_HELP, Values from typing import List, Optional +from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.rich import print_json from pip._internal.cache import WheelCache @@ -472,17 +473,21 @@ def run(self, options: Values, args: List[str]) -> int: ) env = get_environment(lib_locations) + # Display a summary of installed packages, with extra care to + # display a package name as it was requested by the user. installed.sort(key=operator.attrgetter("name")) - items = [] - for result in installed: - item = result.name - try: - installed_dist = env.get_distribution(item) - if installed_dist is not None: - item = f"{item}-{installed_dist.version}" - except Exception: - pass - items.append(item) + summary = [] + installed_versions = {} + for distribution in env.iter_all_distributions(): + installed_versions[distribution.canonical_name] = distribution.version + for package in installed: + display_name = package.name + version = installed_versions.get(canonicalize_name(display_name), None) + if version: + text = f"{display_name}-{version}" + else: + text = display_name + summary.append(text) if conflicts is not None: self._warn_about_conflicts( @@ -490,7 +495,7 @@ def run(self, options: Values, args: List[str]) -> int: resolver_variant=self.determine_resolver_variant(options), ) - installed_desc = " ".join(items) + installed_desc = " ".join(summary) if installed_desc: write_output( "Successfully installed %s", diff --git a/src/pip/_internal/metadata/importlib/_envs.py b/src/pip/_internal/metadata/importlib/_envs.py index 2df738fc738..c82e1ffc951 100644 --- a/src/pip/_internal/metadata/importlib/_envs.py +++ b/src/pip/_internal/metadata/importlib/_envs.py @@ -181,9 +181,10 @@ def _iter_distributions(self) -> Iterator[BaseDistribution]: yield from finder.find_linked(location) def get_distribution(self, name: str) -> Optional[BaseDistribution]: + canonical_name = canonicalize_name(name) matches = ( distribution for distribution in self.iter_all_distributions() - if distribution.canonical_name == canonicalize_name(name) + if distribution.canonical_name == canonical_name ) return next(matches, None)