diff --git a/news/bugfix.12791.rst b/news/bugfix.12791.rst new file mode 100644 index 00000000000..0aad7b5aecc --- /dev/null +++ b/news/bugfix.12791.rst @@ -0,0 +1,2 @@ +Improve performance of pip install. Fix code to display installed packages at the end of pip install, +was O(n^2) to enumerate packages due to accidental loop in loop. \ No newline at end of file diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index d5b06c8c785..64d047d30ba 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,18 @@ 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) + expected_items = { + canonicalize_name(item.name): item.name for item in installed + } + for distribution in env.iter_all_distributions(): + display_name = expected_items.get(distribution.canonical_name, None) + if display_name is not None: + item = f"{display_name}-{distribution.version}" + items.append(item) if conflicts is not None: self._warn_about_conflicts( 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)