diff --git a/docs/html/reference/installation-report.md b/docs/html/reference/installation-report.md index 5823205f9..e0cfcd97e 100644 --- a/docs/html/reference/installation-report.md +++ b/docs/html/reference/installation-report.md @@ -56,6 +56,9 @@ package with the following properties: URL reference. `false` if the requirements was provided as a name and version specifier. +- `is_yanked`: `true` if the requirement was yanked from the index, but was still + selected by pip conform to [PEP 592](https://peps.python.org/pep-0592/#installers). + - `download_info`: Information about the artifact (to be) downloaded for installation, using the [direct URL data structure](https://packaging.python.org/en/latest/specifications/direct-url-data-structure/). @@ -106,6 +109,7 @@ will produce an output similar to this (metadata abriged for brevity): } }, "is_direct": false, + "is_yanked": false, "requested": true, "metadata": { "name": "pydantic", @@ -133,6 +137,7 @@ will produce an output similar to this (metadata abriged for brevity): } }, "is_direct": true, + "is_yanked": false, "requested": true, "metadata": { "name": "packaging", diff --git a/news/12224.feature.rst b/news/12224.feature.rst new file mode 100644 index 000000000..d87426578 --- /dev/null +++ b/news/12224.feature.rst @@ -0,0 +1 @@ +Add ``is_yanked`` boolean entry to the installation report (``--report``) to indicate whether the requirement was yanked from the index, but was still selected by pip conform to PEP 592. diff --git a/src/pip/_internal/models/installation_report.py b/src/pip/_internal/models/installation_report.py index 7f001f35e..e38e8f1c0 100644 --- a/src/pip/_internal/models/installation_report.py +++ b/src/pip/_internal/models/installation_report.py @@ -23,6 +23,9 @@ class InstallationReport: # includes editable requirements), and false if the requirement was # downloaded from a PEP 503 index or --find-links. "is_direct": ireq.is_direct, + # is_yanked is true if the requirement was yanked from the index, but + # was still selected by pip to conform to PEP 592. + "is_yanked": ireq.link.is_yanked if ireq.link else False, # requested is true if the requirement was specified by the user (aka # top level requirement), and false if it was installed as a dependency of a # requirement. https://peps.python.org/pep-0376/#requested diff --git a/tests/functional/test_install_report.py b/tests/functional/test_install_report.py index 003b29d38..a0f855978 100644 --- a/tests/functional/test_install_report.py +++ b/tests/functional/test_install_report.py @@ -64,6 +64,59 @@ def test_install_report_dep( 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.network def test_install_report_index(script: PipTestEnvironment, tmp_path: Path) -> None: """Test report for sdist obtained from index."""