-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Installation/resolution report (aka pip install --dry-run --report) #10771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e371787
fbb8f65
d6685d0
2c84a1c
d32a62b
7fdff17
6eab8e4
0198eae
2841f46
1fbfdc4
6529635
e41b134
16029fb
074c6b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
# Installation Report | ||
|
||
The `--report` option of the pip install command produces a detailed JSON report of what | ||
it did install (or what it would have installed, if used with the `--dry-run` option). | ||
|
||
## Specification | ||
|
||
The report is a JSON object with the following properties: | ||
|
||
- `version`: the string `0`, denoting that the installation report is an experimental | ||
feature. This value will change to `1`, when the feature is deemed stable after | ||
gathering user feedback (likely in pip 22.3 or 23.0). Backward incompatible changes | ||
may be introduced in version `1` without notice. After that, it will change only if | ||
and when backward incompatible changes are introduced, such as removing mandatory | ||
fields or changing the semantics or data type of existing fields. The introduction of | ||
backward incompatible changes will follow the usual pip processes such as the | ||
deprecation cycle or feature flags. Tools must check this field to ensure they support | ||
the corresponding version. | ||
|
||
- `pip_version`: a string with the version of pip used to produce the report. | ||
|
||
- `install`: an array of [InstallationReportItem](InstallationReportItem) representing | ||
the distribution packages (to be) installed. | ||
|
||
- `environment`: an object describing the environment where the installation report was | ||
generated. See [PEP 508 environment | ||
markers](https://peps.python.org/pep-0508/#environment-markers) for more information. | ||
Values have a string type. | ||
|
||
(InstallationReportItem)= | ||
|
||
An `InstallationReportItem` is an object describing a (to be) installed distribution | ||
package with the following properties: | ||
|
||
- `metadata`: the metadata of the distribution, converted to a JSON object according to | ||
the [PEP 566 | ||
transformation](https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata). | ||
|
||
- `is_direct`: `true` if the requirement was provided as, or constrained to, a direct | ||
URL reference. `false` if the requirements was provided as a name and version | ||
specifier. | ||
|
||
- `download_info`: Information about the artifact (to be) downloaded for installation, | ||
using the [direct | ||
URL](https://packaging.python.org/en/latest/specifications/direct-url/) data | ||
structure. When `is_direct` is `true`, this field is the same as the `direct_url.json` | ||
metadata, otherwise it represents the URL of the artifact obtained from the index or | ||
`--find-links`. | ||
|
||
```{note} | ||
For source archives, `download_info.archive_info.hash` may | ||
be absent when the requirement was installed from the wheel cache | ||
and the cache entry was populated by an older pip version that did not | ||
record the origin URL of the downloaded artifact. | ||
``` | ||
|
||
- `requested`: `true` if the requirement was explicitly provided by the user, either | ||
directely via a command line argument or indirectly via a requirements file. `false` | ||
if the requirement was installed as a dependency of another requirement. | ||
|
||
- `requested_extras`: extras requested by the user. This field is only present when the | ||
`requested` field is true. | ||
|
||
## Example | ||
|
||
The following command: | ||
|
||
```console | ||
pip install \ | ||
--ignore-installed --dry-run --quiet \ | ||
--report - \ | ||
"pydantic>=1.9" git+https://github.com/pypa/packaging@main | ||
``` | ||
|
||
will produce an output similar to this (metadata abriged for brevity): | ||
|
||
```json | ||
{ | ||
"version": "0", | ||
"pip_version": "22.2", | ||
"install": [ | ||
{ | ||
"download_info": { | ||
"url": "https://files.pythonhosted.org/packages/a4/0c/fbaa7319dcb5eecd3484686eb5a5c5702a6445adb566f01aee6de3369bc4/pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", | ||
"archive_info": { | ||
"hash": "sha256=18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310" | ||
} | ||
}, | ||
"is_direct": false, | ||
"requested": true, | ||
"metadata": { | ||
"name": "pydantic", | ||
"version": "1.9.1", | ||
"requires_dist": [ | ||
"typing-extensions (>=3.7.4.3)", | ||
"dataclasses (>=0.6) ; python_version < \"3.7\"", | ||
"python-dotenv (>=0.10.4) ; extra == 'dotenv'", | ||
"email-validator (>=1.0.3) ; extra == 'email'" | ||
], | ||
"requires_python": ">=3.6.1", | ||
"provides_extra": [ | ||
"dotenv", | ||
"email" | ||
] | ||
} | ||
}, | ||
{ | ||
"download_info": { | ||
"url": "https://github.com/pypa/packaging", | ||
"vcs_info": { | ||
"vcs": "git", | ||
"requested_revision": "main", | ||
"commit_id": "4f42225e91a0be634625c09e84dd29ea82b85e27" | ||
} | ||
}, | ||
"is_direct": true, | ||
"requested": true, | ||
"metadata": { | ||
"name": "packaging", | ||
"version": "21.4.dev0", | ||
"requires_dist": [ | ||
"pyparsing (!=3.0.5,>=2.0.2)" | ||
], | ||
"requires_python": ">=3.7" | ||
} | ||
}, | ||
{ | ||
"download_info": { | ||
"url": "https://files.pythonhosted.org/packages/6c/10/a7d0fa5baea8fe7b50f448ab742f26f52b80bfca85ac2be9d35cdd9a3246/pyparsing-3.0.9-py3-none-any.whl", | ||
"archive_info": { | ||
"hash": "sha256=5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" | ||
} | ||
}, | ||
"is_direct": false, | ||
"requested": false, | ||
"metadata": { | ||
"name": "pyparsing", | ||
"version": "3.0.9", | ||
"requires_dist": [ | ||
"railroad-diagrams ; extra == \"diagrams\"", | ||
"jinja2 ; extra == \"diagrams\"" | ||
], | ||
"requires_python": ">=3.6.8" | ||
} | ||
}, | ||
{ | ||
"download_info": { | ||
"url": "https://files.pythonhosted.org/packages/75/e1/932e06004039dd670c9d5e1df0cd606bf46e29a28e65d5bb28e894ea29c9/typing_extensions-4.2.0-py3-none-any.whl", | ||
"archive_info": { | ||
"hash": "sha256=6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708" | ||
} | ||
}, | ||
"is_direct": false, | ||
"requested": false, | ||
"metadata": { | ||
"name": "typing_extensions", | ||
"version": "4.2.0", | ||
"requires_python": ">=3.7" | ||
} | ||
} | ||
], | ||
"environment": { | ||
"implementation_name": "cpython", | ||
"implementation_version": "3.10.5", | ||
"os_name": "posix", | ||
"platform_machine": "x86_64", | ||
"platform_release": "5.13-generic", | ||
"platform_system": "Linux", | ||
"platform_version": "...", | ||
"python_full_version": "3.10.5", | ||
"platform_python_implementation": "CPython", | ||
"python_version": "3.10", | ||
"sys_platform": "linux" | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Add an experimental ``--report`` option to the install command to generate a JSON report | ||
of what was installed. In combination with ``--dry-run`` and ``--ignore-installed`` it | ||
can be used to resolve the requirements. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from typing import Any, Dict, Sequence | ||
|
||
from pip._vendor.packaging.markers import default_environment | ||
|
||
from pip import __version__ | ||
from pip._internal.req.req_install import InstallRequirement | ||
|
||
|
||
class InstallationReport: | ||
def __init__(self, install_requirements: Sequence[InstallRequirement]): | ||
self._install_requirements = install_requirements | ||
|
||
@classmethod | ||
def _install_req_to_dict(cls, ireq: InstallRequirement) -> Dict[str, Any]: | ||
assert ireq.download_info, f"No download_info for {ireq}" | ||
res = { | ||
# PEP 610 json for the download URL. download_info.archive_info.hash may | ||
# be absent when the requirement was installed from the wheel cache | ||
# and the cache entry was populated by an older pip version that did not | ||
# record origin.json. | ||
"download_info": ireq.download_info.to_dict(), | ||
# is_direct is true if the requirement was a direct URL reference (which | ||
# includes editable requirements), and false if the requirement was | ||
# downloaded from a PEP 503 index or --find-links. | ||
"is_direct": bool(ireq.original_link), | ||
# 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 | ||
"requested": ireq.user_supplied, | ||
Comment on lines
+22
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These fields are not strictly needed, right? They can be inferred by other entries’ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are needed. |
||
# PEP 566 json encoding for metadata | ||
# https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata | ||
"metadata": ireq.get_dist().metadata_dict, | ||
} | ||
if ireq.user_supplied and ireq.extras: | ||
# For top level requirements, the list of requested extras, if any. | ||
res["requested_extras"] = list(sorted(ireq.extras)) | ||
return res | ||
|
||
def to_dict(self) -> Dict[str, Any]: | ||
return { | ||
"version": "0", | ||
"pip_version": __version__, | ||
"install": [ | ||
self._install_req_to_dict(ireq) for ireq in self._install_requirements | ||
], | ||
# https://peps.python.org/pep-0508/#environment-markers | ||
# TODO: currently, the resolver uses the default environment to evaluate | ||
# environment markers, so that is what we report here. In the future, it | ||
# should also take into account options such as --python-version or | ||
# --platform, perhaps under the form of an environment_override field? | ||
# https://github.com/pypa/pip/issues/11198 | ||
"environment": default_environment(), | ||
} |
Uh oh!
There was an error while loading. Please reload this page.