1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

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

This applies to the new importlib.metadata backend. The legacy
pkg_resources backend already does this (albeit accidentally).

A package inside a wheel is not guaranteed to "work" when directly
imported, so we should not treat it as an installed distribution.
This commit is contained in:
Tzu-ping Chung 2022-06-30 21:43:00 +08:00
parent 06f79b8ee6
commit 6eaa8e9135
3 changed files with 48 additions and 0 deletions

4
news/11217.bugfix.rst Normal file
View file

@ -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.

View file

@ -10,12 +10,24 @@ from typing import Iterator, List, Optional, Sequence, Set, Tuple
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.
@ -36,6 +48,11 @@ class _DistributionFinder:
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]):

View file

@ -1,4 +1,5 @@
import logging
import os
from pathlib import Path
from typing import cast
from unittest import mock
@ -9,6 +10,7 @@ from pip._vendor.packaging.utils import NormalizedName
from pip._internal.metadata import (
BaseDistribution,
get_directory_distribution,
get_environment,
get_wheel_distribution,
)
from pip._internal.metadata.base import FilesystemWheel
@ -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)