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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
import json
import os
import re
import shutil
import sys
import time
import pytest # type: ignore[import-not-found]
from helpers import (
PIPX_METADATA_LEGACY_VERSIONS,
app_name,
assert_package_metadata,
create_package_info_ref,
mock_legacy_venv,
remove_venv_interpreter,
run_pipx_cli,
skip_if_windows,
)
from package_info import PKG
from pipx import constants, paths, shared_libs
from pipx.pipx_metadata_file import PackageInfo, _json_decoder_object_hook
def test_cli(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "nothing has been installed with pipx" in captured.err
@skip_if_windows
def test_cli_global(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["install", "pycowsay"])
captured = capsys.readouterr()
assert "installed package" in captured.out
assert not run_pipx_cli(["list", "--global"])
captured = capsys.readouterr()
assert "nothing has been installed with pipx" in captured.err
def test_missing_interpreter(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["install", "pycowsay"])
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "package pycowsay has invalid interpreter" not in captured.err
remove_venv_interpreter("pycowsay")
assert run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "package pycowsay has invalid interpreter" in captured.err
def test_list_suffix(pipx_temp_env, monkeypatch, capsys):
suffix = "_x"
assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"])
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert f"package pycowsay 0.0.0.2 (pycowsay{suffix})," in captured.out
@pytest.mark.parametrize("metadata_version", PIPX_METADATA_LEGACY_VERSIONS)
def test_list_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version):
assert not run_pipx_cli(["install", "pycowsay"])
mock_legacy_venv("pycowsay", metadata_version=metadata_version)
if metadata_version is None:
assert run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "package pycowsay has missing internal pipx metadata" in captured.err
else:
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "package pycowsay 0.0.0.2," in captured.out
@pytest.mark.parametrize("metadata_version", ["0.1"])
def test_list_suffix_legacy_venv(pipx_temp_env, monkeypatch, capsys, metadata_version):
suffix = "_x"
assert not run_pipx_cli(["install", "pycowsay", f"--suffix={suffix}"])
mock_legacy_venv(f"pycowsay{suffix}", metadata_version=metadata_version)
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert f"package pycowsay 0.0.0.2 (pycowsay{suffix})," in captured.out
def test_list_json(pipx_temp_env, capsys):
pipx_venvs_dir = paths.ctx.home / "venvs"
venv_bin_dir = "Scripts" if constants.WINDOWS else "bin"
assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]])
assert not run_pipx_cli(["install", PKG["pylint"]["spec"]])
assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]])
captured = capsys.readouterr()
assert not run_pipx_cli(["list", "--json"])
captured = capsys.readouterr()
assert not re.search(r"\S", captured.err)
json_parsed = json.loads(captured.out, object_hook=_json_decoder_object_hook)
# raises error if not valid json
assert sorted(json_parsed["venvs"].keys()) == ["pycowsay", "pylint"]
# pycowsay venv
pycowsay_package_ref = create_package_info_ref("pycowsay", "pycowsay", pipx_venvs_dir)
assert_package_metadata(
PackageInfo(**json_parsed["venvs"]["pycowsay"]["metadata"]["main_package"]),
pycowsay_package_ref,
)
assert json_parsed["venvs"]["pycowsay"]["metadata"]["injected_packages"] == {}
# pylint venv
pylint_package_ref = create_package_info_ref(
"pylint",
"pylint",
pipx_venvs_dir,
**{
"app_paths_of_dependencies": {
"dill": [
pipx_venvs_dir / "pylint" / venv_bin_dir / "get_gprof",
pipx_venvs_dir / "pylint" / venv_bin_dir / "get_objgraph",
pipx_venvs_dir / "pylint" / venv_bin_dir / "undill",
],
"isort": [
pipx_venvs_dir / "pylint" / venv_bin_dir / app_name("isort"),
pipx_venvs_dir / "pylint" / venv_bin_dir / app_name("isort-identify-imports"),
],
}
},
)
assert_package_metadata(
PackageInfo(**json_parsed["venvs"]["pylint"]["metadata"]["main_package"]),
pylint_package_ref,
)
assert sorted(json_parsed["venvs"]["pylint"]["metadata"]["injected_packages"].keys()) == ["black"]
black_package_ref = create_package_info_ref("pylint", "black", pipx_venvs_dir, include_apps=False)
assert_package_metadata(
PackageInfo(**json_parsed["venvs"]["pylint"]["metadata"]["injected_packages"]["black"]),
black_package_ref,
)
def test_list_short(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]])
assert not run_pipx_cli(["install", PKG["pylint"]["spec"]])
captured = capsys.readouterr()
assert not run_pipx_cli(["list", "--short"])
captured = capsys.readouterr()
assert "pycowsay 0.0.0.2" in captured.out
assert "pylint 3.0.4" in captured.out
def test_list_standalone_interpreter(pipx_temp_env, monkeypatch, mocked_github_api, capsys):
def which(name):
return None
monkeypatch.setattr(shutil, "which", which)
major = sys.version_info.major
minor = sys.version_info.minor
target_python = f"{major}.{minor}"
assert not run_pipx_cli(
[
"install",
"--fetch-missing-python",
"--python",
target_python,
PKG["pycowsay"]["spec"],
]
)
captured = capsys.readouterr()
assert not run_pipx_cli(["list"])
captured = capsys.readouterr()
assert "standalone" in captured.out
def test_list_does_not_trigger_maintenance(pipx_temp_env, caplog):
assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]])
assert not run_pipx_cli(["install", PKG["pylint"]["spec"]])
now = time.time()
shared_libs.shared_libs.create(verbose=True, pip_args=[])
shared_libs.shared_libs.has_been_updated_this_run = False
access_time = now # this can be anything
os.utime(
shared_libs.shared_libs.pip_path,
(access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now),
)
assert shared_libs.shared_libs.needs_upgrade
run_pipx_cli(["list"])
assert not shared_libs.shared_libs.has_been_updated_this_run
assert shared_libs.shared_libs.needs_upgrade
# same test with --skip-maintenance, which is a no-op
# we expect the same result, along with a warning
os.utime(
shared_libs.shared_libs.pip_path,
(access_time, -shared_libs.SHARED_LIBS_MAX_AGE_SEC - 5 * 60 + now),
)
shared_libs.shared_libs.has_been_updated_this_run = False
assert shared_libs.shared_libs.needs_upgrade
run_pipx_cli(["list", "--skip-maintenance"])
assert not shared_libs.shared_libs.has_been_updated_this_run
assert shared_libs.shared_libs.needs_upgrade
def test_list_pinned_packages(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["install", PKG["pycowsay"]["spec"]])
assert not run_pipx_cli(["install", PKG["black"]["spec"]])
captured = capsys.readouterr()
assert not run_pipx_cli(["pin", "black"])
assert not run_pipx_cli(["list", "--pinned"])
captured = capsys.readouterr()
assert "black 22.8.0" in captured.out
assert "pycowsay 0.0.0.2" not in captured.out
def test_list_pinned_packages_include_injected(pipx_temp_env, monkeypatch, capsys):
assert not run_pipx_cli(["install", PKG["pylint"]["spec"], PKG["nox"]["spec"]])
assert not run_pipx_cli(["inject", "pylint", PKG["black"]["spec"]])
assert not run_pipx_cli(["pin", "pylint"])
assert not run_pipx_cli(["pin", "nox"])
captured = capsys.readouterr()
assert not run_pipx_cli(["list", "--pinned", "--include-injected"])
captured = capsys.readouterr()
assert "nox 2023.4.22" in captured.out
assert "pylint 3.0.4" in captured.out
assert "black 22.8.0 (injected in venv pylint)" in captured.out
|