File: test_non_host.py

package info (click to toggle)
python-pipdeptree 2.30.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 348 kB
  • sloc: python: 3,286; sh: 28; makefile: 5
file content (157 lines) | stat: -rw-r--r-- 5,547 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from __future__ import annotations

import sys
from platform import python_implementation
from typing import TYPE_CHECKING
from unittest.mock import Mock

import pytest
import virtualenv

from pipdeptree.__main__ import main

if TYPE_CHECKING:
    from pathlib import Path

    from pytest_mock import MockerFixture


@pytest.fixture(scope="session")
def expected_venv_pkgs() -> frozenset[str]:
    implementation = python_implementation()
    if implementation == "CPython":  # pragma: cpython cover
        expected = {"pip", "setuptools"}
    elif implementation == "PyPy":  # pragma: pypy cover
        expected = {"cffi", "greenlet", "pip", "hpy", "setuptools"}
    else:  # pragma: no cover
        raise ValueError(implementation)
    if sys.version_info >= (3, 12):  # pragma: >=3.12 cover
        expected -= {"setuptools"}

    return frozenset(expected)


@pytest.mark.parametrize("args_joined", [True, False])
def test_custom_interpreter(
    tmp_path: Path,
    mocker: MockerFixture,
    monkeypatch: pytest.MonkeyPatch,
    capfd: pytest.CaptureFixture[str],
    args_joined: bool,
    expected_venv_pkgs: frozenset[str],
) -> None:
    # Delete $PYTHONPATH so that it cannot be passed to the custom interpreter process (since we don't know what
    # distribution metadata to expect when it's used).
    monkeypatch.delenv("PYTHONPATH", False)

    monkeypatch.chdir(tmp_path)
    result = virtualenv.cli_run([str(tmp_path / "venv"), "--activators", ""])
    py = str(result.creator.exe.relative_to(tmp_path))
    cmd = ["", f"--python={result.creator.exe}"] if args_joined else ["", "--python", py]
    cmd += ["--all", "--depth", "0"]
    mocker.patch("pipdeptree._discovery.sys.argv", cmd)
    main()
    out, _ = capfd.readouterr()
    found = {i.split("==")[0] for i in out.splitlines()}

    assert expected_venv_pkgs == found, out


def test_custom_interpreter_with_local_only(
    tmp_path: Path,
    mocker: MockerFixture,
    capfd: pytest.CaptureFixture[str],
) -> None:
    venv_path = str(tmp_path / "venv")
    result = virtualenv.cli_run([venv_path, "--system-site-packages", "--activators", ""])

    cmd = ["", f"--python={result.creator.exe}", "--local-only"]
    mocker.patch("pipdeptree._discovery.sys.prefix", venv_path)
    mocker.patch("pipdeptree._discovery.sys.argv", cmd)
    main()
    out, _ = capfd.readouterr()
    found = {i.split("==")[0] for i in out.splitlines()}
    expected = {"pip", "setuptools"}
    if sys.version_info >= (3, 12):  # pragma: >=3.12 cover
        expected -= {"setuptools"}
    assert expected == found, out


def test_custom_interpreter_with_user_only(
    tmp_path: Path, mocker: MockerFixture, capfd: pytest.CaptureFixture[str]
) -> None:
    # ensures there is no output when --user-only and --python are passed

    venv_path = str(tmp_path / "venv")
    result = virtualenv.cli_run([venv_path, "--activators", ""])

    cmd = ["", f"--python={result.creator.exe}", "--user-only"]
    mocker.patch("pipdeptree.__main__.sys.argv", cmd)
    main()
    out, err = capfd.readouterr()
    assert not err

    # Here we expect 1 element because print() adds a newline.
    found = out.splitlines()
    assert len(found) == 1
    assert not found[0]


def test_custom_interpreter_with_user_only_and_system_site_pkgs_enabled(
    tmp_path: Path,
    fake_dist: Path,
    mocker: MockerFixture,
    monkeypatch: pytest.MonkeyPatch,
    capfd: pytest.CaptureFixture[str],
) -> None:
    # ensures that we provide user site metadata when --user-only and --python are passed and the custom interpreter has
    # system site packages enabled

    # Make a fake user site directory since we don't know what to expect from the real one.
    fake_user_site = str(fake_dist.parent)
    mocker.patch("pipdeptree._discovery.site.getusersitepackages", Mock(return_value=fake_user_site))

    # Create a temporary virtual environment.
    venv_path = str(tmp_path / "venv")
    result = virtualenv.cli_run([venv_path, "--activators", ""])

    # Use $PYTHONPATH to add the fake user site into the custom interpreter's environment so that it will include it in
    # its sys.path.
    monkeypatch.setenv("PYTHONPATH", str(fake_user_site))

    cmd = ["", f"--python={result.creator.exe}", "--user-only"]
    mocker.patch("pipdeptree.__main__.sys.argv", cmd)
    main()

    out, err = capfd.readouterr()
    assert not err
    found = {i.split("==")[0] for i in out.splitlines()}
    expected = {"bar"}

    assert expected == found


def test_custom_interpreter_ensure_pythonpath_envar_is_honored(
    tmp_path: Path,
    mocker: MockerFixture,
    monkeypatch: pytest.MonkeyPatch,
    capfd: pytest.CaptureFixture[str],
    expected_venv_pkgs: frozenset[str],
) -> None:
    # ensures that we honor $PYTHONPATH when passing it to the custom interpreter process
    venv_path = str(tmp_path / "venv")
    result = virtualenv.cli_run([venv_path, "--activators", ""])

    another_path = tmp_path / "another-path"
    fake_dist = another_path / "foo-1.2.3.dist-info"
    fake_dist.mkdir(parents=True)
    fake_metadata = fake_dist / "METADATA"
    with fake_metadata.open("w") as f:
        f.write("Metadata-Version: 2.3\nName: foo\nVersion: 1.2.3\n")
    cmd = ["", f"--python={result.creator.exe}", "--all", "--depth", "0"]
    mocker.patch("pipdeptree._discovery.sys.argv", cmd)
    monkeypatch.setenv("PYTHONPATH", str(another_path))
    main()
    out, _ = capfd.readouterr()
    found = {i.split("==")[0] for i in out.splitlines()}
    assert {*expected_venv_pkgs, "foo"} == found, out