From 6817fbfb1fd389ad61009f0199db5670b146c8d3 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Sat, 6 Aug 2022 06:18:59 +0800 Subject: [PATCH] Skip dist if metadata does not have a valid name --- news/11352.bugfix.rst | 2 ++ src/pip/_internal/metadata/importlib/_compat.py | 14 +++++++++++++- src/pip/_internal/metadata/importlib/_envs.py | 14 +++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 news/11352.bugfix.rst diff --git a/news/11352.bugfix.rst b/news/11352.bugfix.rst new file mode 100644 index 00000000000..78016c912ef --- /dev/null +++ b/news/11352.bugfix.rst @@ -0,0 +1,2 @@ +Ignore distributions with invalid ``Name`` in metadata instead of crashing, when +using the ``importlib.metadata`` backend. diff --git a/src/pip/_internal/metadata/importlib/_compat.py b/src/pip/_internal/metadata/importlib/_compat.py index e0879807ab9..593bff23ede 100644 --- a/src/pip/_internal/metadata/importlib/_compat.py +++ b/src/pip/_internal/metadata/importlib/_compat.py @@ -2,6 +2,15 @@ from typing import Any, Optional, Protocol, cast +class BadMetadata(ValueError): + def __init__(self, dist: importlib.metadata.Distribution, *, reason: str) -> None: + self.dist = dist + self.reason = reason + + def __str__(self) -> str: + return f"Bad metadata in {self.dist} ({self.reason})" + + class BasePath(Protocol): """A protocol that various path objects conform. @@ -40,4 +49,7 @@ def get_dist_name(dist: importlib.metadata.Distribution) -> str: The ``name`` attribute is only available in Python 3.10 or later. We are targeting exactly that, but Mypy does not know this. """ - return cast(Any, dist).name + name = cast(Any, dist).name + if not isinstance(name, str): + raise BadMetadata(dist, reason="invalid metadata entry 'name'") + return name diff --git a/src/pip/_internal/metadata/importlib/_envs.py b/src/pip/_internal/metadata/importlib/_envs.py index d5fcfdbfef2..cbec59e2c6d 100644 --- a/src/pip/_internal/metadata/importlib/_envs.py +++ b/src/pip/_internal/metadata/importlib/_envs.py @@ -1,5 +1,6 @@ import functools import importlib.metadata +import logging import os import pathlib import sys @@ -14,9 +15,11 @@ from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filetypes import WHEEL_EXTENSION -from ._compat import BasePath, get_dist_name, get_info_location +from ._compat import BadMetadata, BasePath, get_dist_name, get_info_location from ._dists import Distribution +logger = logging.getLogger(__name__) + def _looks_like_wheel(location: str) -> bool: if not location.endswith(WHEEL_EXTENSION): @@ -56,11 +59,16 @@ def _find_impl(self, location: str) -> Iterator[FoundResult]: # To know exactly where we find a distribution, we have to feed in the # paths one by one, instead of dumping the list to importlib.metadata. for dist in importlib.metadata.distributions(path=[location]): - normalized_name = canonicalize_name(get_dist_name(dist)) + info_location = get_info_location(dist) + try: + raw_name = get_dist_name(dist) + except BadMetadata as e: + logger.warning("Skipping %s due to %s", info_location, e.reason) + continue + normalized_name = canonicalize_name(raw_name) if normalized_name in self._found_names: continue self._found_names.add(normalized_name) - info_location = get_info_location(dist) yield dist, info_location def find(self, location: str) -> Iterator[BaseDistribution]: