import json import textwrap from pathlib import Path from typing import Any, Dict, Tuple import pytest from packaging.utils import canonicalize_name from ..lib import PipTestEnvironment, TestData def _install_dict(report: Dict[str, Any]) -> Dict[str, Any]: return {canonicalize_name(i["metadata"]["name"]): i for i in report["install"]} def test_install_report_basic( script: PipTestEnvironment, shared_data: TestData, tmp_path: Path ) -> None: report_path = tmp_path / "report.json" script.pip( "install", "simplewheel", "--dry-run", "--no-index", "--find-links", str(shared_data.root / "packages/"), "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert "install" in report assert len(report["install"]) == 1 simplewheel_report = _install_dict(report)["simplewheel"] assert simplewheel_report["metadata"]["name"] == "simplewheel" assert simplewheel_report["requested"] is True assert simplewheel_report["is_direct"] is False url = simplewheel_report["download_info"]["url"] assert url.startswith("file://") assert url.endswith("/packages/simplewheel-2.0-1-py2.py3-none-any.whl") assert ( simplewheel_report["download_info"]["archive_info"]["hash"] == "sha256=191d6520d0570b13580bf7642c97ddfbb46dd04da5dd2cf7bef9f32391dfe716" ) def test_install_report_dep( script: PipTestEnvironment, shared_data: TestData, tmp_path: Path ) -> None: """Test dependencies are present in the install report with requested=False.""" report_path = tmp_path / "report.json" script.pip( "install", "require_simple", "--dry-run", "--no-index", "--find-links", str(shared_data.root / "packages/"), "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 2 assert _install_dict(report)["require-simple"]["requested"] is True assert _install_dict(report)["simple"]["requested"] is False def test_yanked_version( script: PipTestEnvironment, data: TestData, tmp_path: Path ) -> None: """ Test is_yanked is True when explicitly requesting a yanked package. Yanked files are always ignored, unless they are the only file that matches a version specifier that "pins" to an exact version (PEP 592). """ report_path = tmp_path / "report.json" script.pip( "install", "simple==3.0", "--index-url", data.index_url("yanked"), "--dry-run", "--report", str(report_path), allow_stderr_warning=True, ) report = json.loads(report_path.read_text()) simple_report = _install_dict(report)["simple"] assert simple_report["requested"] is True assert simple_report["is_direct"] is False assert simple_report["is_yanked"] is True assert simple_report["metadata"]["version"] == "3.0" def test_skipped_yanked_version( script: PipTestEnvironment, data: TestData, tmp_path: Path ) -> None: """ Test is_yanked is False when not explicitly requesting a yanked package. Yanked files are always ignored, unless they are the only file that matches a version specifier that "pins" to an exact version (PEP 592). """ report_path = tmp_path / "report.json" script.pip( "install", "simple", "--index-url", data.index_url("yanked"), "--dry-run", "--report", str(report_path), ) report = json.loads(report_path.read_text()) simple_report = _install_dict(report)["simple"] assert simple_report["requested"] is True assert simple_report["is_direct"] is False assert simple_report["is_yanked"] is False assert simple_report["metadata"]["version"] == "2.0" @pytest.mark.parametrize( "specifiers", [ # result should be the same regardless of the method and order in which # extras are specified ("Paste[openid]==1.7.5.1",), ("Paste==1.7.5.1", "Paste[openid]==1.7.5.1"), ("Paste[openid]==1.7.5.1", "Paste==1.7.5.1"), ], ) @pytest.mark.network def test_install_report_index( script: PipTestEnvironment, tmp_path: Path, specifiers: Tuple[str, ...] ) -> None: """Test report for sdist obtained from index.""" report_path = tmp_path / "report.json" script.pip( "install", "--dry-run", *specifiers, "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 2 install_dict = _install_dict(report) assert install_dict["paste"]["requested"] is True assert install_dict["python-openid"]["requested"] is False paste_report = install_dict["paste"] assert paste_report["download_info"]["url"].startswith( "https://files.pythonhosted.org/" ) assert paste_report["download_info"]["url"].endswith("/Paste-1.7.5.1.tar.gz") assert ( paste_report["download_info"]["archive_info"]["hash"] == "sha256=11645842ba8ec986ae8cfbe4c6cacff5c35f0f4527abf4f5581ae8b4ad49c0b6" ) assert paste_report["requested_extras"] == ["openid"] assert "requires_dist" in paste_report["metadata"] @pytest.mark.network def test_install_report_index_multiple_extras( script: PipTestEnvironment, tmp_path: Path ) -> None: """Test report for sdist obtained from index, with multiple extras requested.""" report_path = tmp_path / "report.json" script.pip( "install", "--dry-run", "Paste[openid]", "Paste[subprocess]", "--report", str(report_path), ) report = json.loads(report_path.read_text()) install_dict = _install_dict(report) assert "paste" in install_dict assert install_dict["paste"]["requested_extras"] == ["openid", "subprocess"] @pytest.mark.network def test_install_report_direct_archive( script: PipTestEnvironment, tmp_path: Path, shared_data: TestData ) -> None: """Test report for direct URL archive.""" report_path = tmp_path / "report.json" script.pip( "install", str(shared_data.root / "packages" / "simplewheel-1.0-py2.py3-none-any.whl"), "--dry-run", "--no-index", "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert "install" in report assert len(report["install"]) == 1 simplewheel_report = _install_dict(report)["simplewheel"] assert simplewheel_report["metadata"]["name"] == "simplewheel" assert simplewheel_report["requested"] is True assert simplewheel_report["is_direct"] is True url = simplewheel_report["download_info"]["url"] assert url.startswith("file://") assert url.endswith("/packages/simplewheel-1.0-py2.py3-none-any.whl") assert ( simplewheel_report["download_info"]["archive_info"]["hash"] == "sha256=e63aa139caee941ec7f33f057a5b987708c2128238357cf905429846a2008718" ) assert simplewheel_report["download_info"]["archive_info"]["hashes"] == { "sha256": "e63aa139caee941ec7f33f057a5b987708c2128238357cf905429846a2008718" } @pytest.mark.network def test_install_report_vcs_and_wheel_cache( script: PipTestEnvironment, tmp_path: Path ) -> None: """Test report for VCS reference, and interactions with the wheel cache.""" cache_dir = tmp_path / "cache" report_path = tmp_path / "report.json" script.pip( "install", "git+https://github.com/pypa/pip-test-package" "@5547fa909e83df8bd743d3978d6667497983a4b7", "--cache-dir", str(cache_dir), "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 1 pip_test_package_report = report["install"][0] assert pip_test_package_report["is_direct"] is True assert pip_test_package_report["requested"] is True assert ( pip_test_package_report["download_info"]["url"] == "https://github.com/pypa/pip-test-package" ) assert pip_test_package_report["download_info"]["vcs_info"]["vcs"] == "git" assert ( pip_test_package_report["download_info"]["vcs_info"]["commit_id"] == "5547fa909e83df8bd743d3978d6667497983a4b7" ) # Now do it again to make sure the cache is used and that the report still contains # the original VCS url. report_path.unlink() result = script.pip( "install", "pip-test-package @ git+https://github.com/pypa/pip-test-package" "@5547fa909e83df8bd743d3978d6667497983a4b7", "--ignore-installed", "--cache-dir", str(cache_dir), "--report", str(report_path), ) assert "Using cached pip_test_package" in result.stdout report = json.loads(report_path.read_text()) assert len(report["install"]) == 1 pip_test_package_report = report["install"][0] assert pip_test_package_report["is_direct"] is True assert pip_test_package_report["requested"] is True assert ( pip_test_package_report["download_info"]["url"] == "https://github.com/pypa/pip-test-package" ) assert pip_test_package_report["download_info"]["vcs_info"]["vcs"] == "git" assert ( pip_test_package_report["download_info"]["vcs_info"]["commit_id"] == "5547fa909e83df8bd743d3978d6667497983a4b7" ) @pytest.mark.network def test_install_report_vcs_editable( script: PipTestEnvironment, tmp_path: Path ) -> None: """Test report remote editable.""" report_path = tmp_path / "report.json" script.pip( "install", "--editable", "git+https://github.com/pypa/pip-test-package" "@5547fa909e83df8bd743d3978d6667497983a4b7" "#egg=pip-test-package", "--report", str(report_path), ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 1 pip_test_package_report = report["install"][0] assert pip_test_package_report["is_direct"] is True assert pip_test_package_report["download_info"]["url"].startswith("file://") assert pip_test_package_report["download_info"]["url"].endswith( "/src/pip-test-package" ) assert pip_test_package_report["download_info"]["dir_info"]["editable"] is True @pytest.mark.network def test_install_report_local_path_with_extras( script: PipTestEnvironment, tmp_path: Path, shared_data: TestData ) -> None: """Test report remote editable.""" project_path = tmp_path / "pkga" project_path.mkdir() project_path.joinpath("pyproject.toml").write_text( textwrap.dedent( """\ [project] name = "pkga" version = "1.0" [project.optional-dependencies] test = ["simple"] """ ) ) report_path = tmp_path / "report.json" script.pip( "install", "--dry-run", "--no-build-isolation", "--no-index", "--find-links", str(shared_data.root / "packages/"), "--report", str(report_path), str(project_path) + "[test]", ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 2 pkga_report = report["install"][0] assert pkga_report["metadata"]["name"] == "pkga" assert pkga_report["is_direct"] is True assert pkga_report["requested"] is True assert pkga_report["requested_extras"] == ["test"] simple_report = report["install"][1] assert simple_report["metadata"]["name"] == "simple" assert simple_report["is_direct"] is False assert simple_report["requested"] is False assert "requested_extras" not in simple_report @pytest.mark.network def test_install_report_editable_local_path_with_extras( script: PipTestEnvironment, tmp_path: Path, shared_data: TestData ) -> None: """Test report remote editable.""" project_path = tmp_path / "pkga" project_path.mkdir() project_path.joinpath("pyproject.toml").write_text( textwrap.dedent( """\ [project] name = "pkga" version = "1.0" [project.optional-dependencies] test = ["simple"] """ ) ) report_path = tmp_path / "report.json" script.pip( "install", "--dry-run", "--no-build-isolation", "--no-index", "--find-links", str(shared_data.root / "packages/"), "--report", str(report_path), "--editable", str(project_path) + "[test]", ) report = json.loads(report_path.read_text()) assert len(report["install"]) == 2 pkga_report = report["install"][0] assert pkga_report["metadata"]["name"] == "pkga" assert pkga_report["is_direct"] is True assert pkga_report["requested"] is True assert pkga_report["requested_extras"] == ["test"] simple_report = report["install"][1] assert simple_report["metadata"]["name"] == "simple" assert simple_report["is_direct"] is False assert simple_report["requested"] is False assert "requested_extras" not in simple_report def test_install_report_to_stdout( script: PipTestEnvironment, shared_data: TestData ) -> None: result = script.pip( "install", "simplewheel", "--quiet", "--dry-run", "--no-index", "--find-links", str(shared_data.root / "packages/"), "--report", "-", ) report = json.loads(result.stdout) assert "install" in report assert len(report["install"]) == 1