Skip to content

Commit

Permalink
Move pip's frozen requirement API into a single module (#402)
Browse files Browse the repository at this point in the history
  • Loading branch information
kemzeb authored Sep 7, 2024
1 parent e945c84 commit bdeea39
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 45 deletions.
13 changes: 13 additions & 0 deletions src/pipdeptree/_adapter.py → src/pipdeptree/_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
from importlib.metadata import Distribution


def dist_to_frozen_repr(dist: Distribution) -> str:
"""Return the frozen requirement repr of a `importlib.metadata.Distribution` object."""
from pip._internal.operations.freeze import FrozenRequirement # noqa: PLC0415, PLC2701

adapter = PipBaseDistributionAdapter(dist)
fr = FrozenRequirement.from_dist(adapter) # type: ignore[arg-type]

return str(fr).strip()


class PipBaseDistributionAdapter:
"""
An adapter class for pip's `pip._internal.metadata.BaseDistribution` abstract class.
Expand Down Expand Up @@ -74,3 +84,6 @@ def editable_project_location(self) -> str | None:
with Path(egg_link_path).open("r", encoding=locale.getpreferredencoding(False)) as f: # noqa: FBT003
result = f.readline().rstrip()
return result


__all__ = ["dist_to_frozen_repr"]
11 changes: 3 additions & 8 deletions src/pipdeptree/_models/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
from packaging.requirements import InvalidRequirement, Requirement
from packaging.utils import canonicalize_name

from pipdeptree._freeze import dist_to_frozen_repr

if TYPE_CHECKING:
from importlib.metadata import Distribution

from pipdeptree._adapter import PipBaseDistributionAdapter


class InvalidRequirementError(ValueError):
"""
Expand Down Expand Up @@ -75,12 +75,7 @@ def render(

@staticmethod
def as_frozen_repr(dist: Distribution) -> str:
from pip._internal.operations.freeze import FrozenRequirement # noqa: PLC0415, PLC2701 # pragma: no cover

adapter = PipBaseDistributionAdapter(dist)
fr = FrozenRequirement.from_dist(adapter) # type: ignore[arg-type]

return str(fr).strip()
return dist_to_frozen_repr(dist)

def __repr__(self) -> str:
return f'<{self.__class__.__name__}("{self.key}")>'
Expand Down
56 changes: 19 additions & 37 deletions tests/_models/test_package.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from __future__ import annotations

import locale
import sys
from importlib.metadata import PackageNotFoundError
from pathlib import Path
from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock, Mock

Expand All @@ -29,18 +26,11 @@ def test_guess_version_setuptools(mocker: MockerFixture) -> None:
assert result == "?"


def test_package_as_frozen_repr(tmp_path: Path, mocker: MockerFixture) -> None:
file_path = tmp_path / "foo.egg-link"
with Path(file_path).open("w", encoding=locale.getpreferredencoding(False)) as f:
f.write("/A/B/foo")
mock_path = sys.path.copy()
mock_path.append(str(tmp_path))
mocker.patch("pipdeptree._discovery.sys.path", mock_path)
json_text = '{"dir_info": {"editable": true}}'
foo = Mock(metadata={"Name": "foo"}, version="20.4.1")
foo.read_text = Mock(return_value=json_text)
def test_package_as_frozen_repr(mocker: MockerFixture) -> None:
foo = Mock(metadata={"Name": "foo"}, version="1.2.3")
dp = DistPackage(foo)
expected = "# Editable install with no version control (foo==20.4.1)\n-e /A/B/foo"
expected = "test"
mocker.patch("pipdeptree._models.package.dist_to_frozen_repr", Mock(return_value=expected))
assert Package.as_frozen_repr(dp.unwrap()) == expected


Expand All @@ -67,8 +57,7 @@ def test_dist_package_requires_with_environment_markers_that_eval_to_false() ->
def test_dist_package_render_as_root() -> None:
foo = Mock(metadata={"Name": "foo"}, version="20.4.1")
dp = DistPackage(foo)
is_frozen = False
assert dp.render_as_root(frozen=is_frozen) == "foo==20.4.1"
assert dp.render_as_root(frozen=False) == "foo==20.4.1"


def test_dist_package_render_as_branch() -> None:
Expand All @@ -78,18 +67,15 @@ def test_dist_package_render_as_branch() -> None:
bar_req.name = "bar"
rp = ReqPackage(bar_req, dist=bar)
dp = DistPackage(foo).as_parent_of(rp)
is_frozen = False
assert dp.render_as_branch(frozen=is_frozen) == "foo==20.4.1 [requires: bar>=4.0]"
assert dp.render_as_branch(frozen=False) == "foo==20.4.1 [requires: bar>=4.0]"


def test_dist_package_render_as_root_with_frozen() -> None:
json_text = '{"dir_info": {"editable": true}, "url": "file:///A/B/foo"}'
foo = Mock(metadata={"Name": "foo"}, version="20.4.1")
foo.read_text = Mock(return_value=json_text)
def test_dist_package_render_as_root_with_frozen(mocker: MockerFixture) -> None:
foo = Mock(metadata={"Name": "foo"}, version="1.2.3")
dp = DistPackage(foo)
is_frozen = True
expect = "# Editable install with no version control (foo==20.4.1)\n-e /A/B/foo"
assert dp.render_as_root(frozen=is_frozen) == expect
expected = "test"
mocker.patch("pipdeptree._models.package.dist_to_frozen_repr", Mock(return_value=expected))
assert dp.render_as_root(frozen=True) == expected


def test_dist_package_as_parent_of() -> None:
Expand Down Expand Up @@ -181,30 +167,26 @@ def test_req_package_render_as_root() -> None:
bar_req = MagicMock(specifier=[">=4.0"])
bar_req.name = "bar"
rp = ReqPackage(bar_req, dist=bar)
is_frozen = False
assert rp.render_as_root(frozen=is_frozen) == "bar==4.1.0"
assert rp.render_as_root(frozen=False) == "bar==4.1.0"


def test_req_package_render_as_root_with_frozen() -> None:
json_text = '{"dir_info": {"editable": true}, "url": "file:///A/B/bar"}'
def test_req_package_render_as_root_with_frozen(mocker: MockerFixture) -> None:
bar = Mock(metadata={"Name": "bar"}, version="4.1.0")
bar.read_text = Mock(return_value=json_text)
d = DistPackage(bar)
dp = DistPackage(bar)
bar_req = MagicMock(specifier=[">=4.0"])
bar_req.name = "bar"
rp = ReqPackage(bar_req, dist=d)
is_frozen = True
expect = "# Editable install with no version control (bar==4.1.0)\n-e /A/B/bar"
assert rp.render_as_root(frozen=is_frozen) == expect
rp = ReqPackage(bar_req, dp)
expected = "test"
mocker.patch("pipdeptree._models.package.dist_to_frozen_repr", Mock(return_value=expected))
assert rp.render_as_root(frozen=True) == expected


def test_req_package_render_as_branch() -> None:
bar = Mock(metadata={"Name": "bar"}, version="4.1.0")
bar_req = MagicMock(specifier=[">=4.0"])
bar_req.name = "bar"
rp = ReqPackage(bar_req, dist=bar)
is_frozen = False
assert rp.render_as_branch(frozen=is_frozen) == "bar [required: >=4.0, installed: 4.1.0]"
assert rp.render_as_branch(frozen=False) == "bar [required: >=4.0, installed: 4.1.0]"


def test_req_package_is_conflicting_handle_dev_versions() -> None:
Expand Down
12 changes: 12 additions & 0 deletions tests/test_freeze.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

from unittest.mock import Mock

from pipdeptree._freeze import dist_to_frozen_repr


def test_dist_to_frozen_repr() -> None:
foo = Mock(metadata={"Name": "foo"}, version="20.4.1")
foo.read_text = Mock(return_value=None)
expected = "foo==20.4.1"
assert dist_to_frozen_repr(foo) == expected

0 comments on commit bdeea39

Please sign in to comment.