Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't consider dist-info in a wheel as "installed" #11217

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions news/11217.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Do not consider a ``.dist-info`` directory found inside a wheel-like zip file
as metadata for an installed distribution. A package in a wheel is (by
definition) not installed, and is not guaranteed to work due to how a wheel is
structured.
17 changes: 17 additions & 0 deletions src/pip/_internal/metadata/importlib/_envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name

from pip._internal.metadata.base import BaseDistribution, BaseEnvironment
from pip._internal.models.wheel import Wheel
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 ._dists import Distribution


def _looks_like_wheel(location: str) -> bool:
if not location.endswith(WHEEL_EXTENSION):
return False
if not os.path.isfile(location):
return False
if not Wheel.wheel_file_re.match(os.path.basename(location)):
return False
return zipfile.is_zipfile(location)


class _DistributionFinder:
"""Finder to locate distributions.

Expand All @@ -36,6 +48,11 @@ def __init__(self) -> None:

def _find_impl(self, location: str) -> Iterator[FoundResult]:
"""Find distributions in a location."""
# Skip looking inside a wheel. Since a package inside a wheel is not
# always valid (due to .data directories etc.), its .dist-info entry
# should not be considered an installed distribution.
if _looks_like_wheel(location):
return
# 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]):
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/metadata/test_metadata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import os
from pathlib import Path
from typing import cast
from unittest import mock
Expand All @@ -9,6 +10,7 @@
from pip._internal.metadata import (
BaseDistribution,
get_directory_distribution,
get_environment,
get_wheel_distribution,
)
from pip._internal.metadata.base import FilesystemWheel
Expand Down Expand Up @@ -102,3 +104,28 @@ def test_metadata_dict(tmp_path: Path) -> None:
metadata_dict = dist.metadata_dict
assert metadata_dict["name"] == "pkga"
assert metadata_dict["version"] == "1.0.1"


def test_no_dist_found_in_wheel(tmp_path: Path) -> None:
location = os.fspath(tmp_path.joinpath("pkg-1-py3-none-any.whl"))
make_wheel(name="pkg", version="1").save_to(location)
assert get_environment([location]).get_distribution("pkg") is None


def test_dist_found_in_directory_named_whl(tmp_path: Path) -> None:
dir_path = tmp_path.joinpath("pkg-1-py3-none-any.whl")
info_path = dir_path.joinpath("pkg-1.dist-info")
info_path.mkdir(parents=True)
info_path.joinpath("METADATA").write_text("Name: pkg")
location = os.fspath(dir_path)
dist = get_environment([location]).get_distribution("pkg")
assert dist is not None and dist.location is not None
assert Path(dist.location) == Path(location)


def test_dist_found_in_zip(tmp_path: Path) -> None:
location = os.fspath(tmp_path.joinpath("pkg.zip"))
make_wheel(name="pkg", version="1").save_to(location)
dist = get_environment([location]).get_distribution("pkg")
assert dist is not None and dist.location is not None
assert Path(dist.location) == Path(location)