Lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm
Project description
pipgrip
pipgrip is a lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm, which is also used by poetry. For one or more PEP 508 dependency specifications, pipgrip recursively fetches Python wheel metadata necessary for version solving (with fallback to building the wheel if no metadata is available), and optionally renders the full resulting dependency tree.
$ pipgrip --tree fastapi~=0.94
fastapi~=0.94 (0.95.1)
├── pydantic!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0,>=1.6.2 (1.10.7)
│ └── typing-extensions>=4.2.0 (4.5.0)
└── starlette<0.27.0,>=0.26.1 (0.26.1)
└── anyio<5,>=3.4.0 (3.6.2)
├── idna>=2.8 (3.4)
└── sniffio>=1.1 (1.3.0)
pipgrip vs. poetry
poetry offers package management with dependency resolution, essentially replacing pip/setuptools. This means that poetry packages don't contain setup.py, and hence are not compatible with pip install -e: poetry projects would have to be converted to setuptools-based projects with e.g. dephell. To avoid such hassle, pipgrip only requires the selected package(s) + dependencies to be available to pip in the usual way.
pipgrip vs. pipdeptree
For offline usage, pipdeptree can inspect the current environment and show how the currently installed packages relate to each other. This however requires the packages to be pip-installed, and (despite warnings about e.g. cyclic dependencies) offers no form of dependency resolution since it's only based on the (single) package versions installed in the environment. Such shortcomings are avoided when using pipgrip, since packages don't need to be installed and all versions available to pip are considered.
Installation
This pure-Python, OS independent package is available on PyPI:
pip install pipgrip
Usage
This package can be used to:
- Render an exhaustive dependency tree for any given pip-compatible package(s):
pipgrip --tree requests
- Alleviate Python dependency hell by resolving the latest viable combination of required packages
- Avoid bugs by running pipgrip as a stage in CI pipelines
- Detect version conflicts for given constraints and give human readable feedback about it
- Warn for cyclic dependencies in local projects [and install them anyway]:
pipgrip -v --tree . [--install -e]
- Install complex packages without worries:
pipgrip --install aiobotocore[awscli]
- Generate a lockfile with a complete working set of dependencies for reproducible installs:
pipgrip --lock aiobotocore[awscli] && pip install aiobotocore[awscli] --constraint ./pipgrip.lock
- Combine dependency trees of multiple packages into one unified set of pinned packages:
pipgrip .[boto3] s3transfer==0.2.1 s3fs smart_open[s3]
See also known caveats.
Optionally, the environment variable PIPGRIP_ADDITIONAL_REQUIREMENTS can be populated with space/newline separated requirements, which will be appended to the requirements passed via CLI.
$ pipgrip --help
Usage: pipgrip [OPTIONS] [DEPENDENCIES]...
pipgrip is a lightweight pip dependency resolver with deptree preview
functionality based on the PubGrub algorithm, which is also used by poetry. For
one or more PEP 508 dependency specifications, pipgrip recursively fetches
Python wheel metadata necessary for version solving (with fallback to building
the wheel if no metadata is available), and optionally renders the full
resulting dependency tree.
Options:
--install Install full dependency tree after resolving.
-e, --editable Install a project in editable mode.
--user Install to the Python user install directory for
your platform -- typically ~/.local/, or
%APPDATA%\Python on Windows.
-r, --requirements-file FILE Install from the given requirements file. This
option can be used multiple times.
--lock Write out pins to './pipgrip.lock'.
--pipe Output space-separated pins instead of newline-
separated pins.
--json Output pins as JSON dict instead of newline-
separated pins. Combine with --tree or --reversed-
tree for a detailed nested JSON dependency tree.
--sort Sort pins alphabetically before writing out. Can
be used bare, or in combination with --lock,
--pipe, --json, --tree-json, or --tree-json-exact.
--tree Output human readable dependency tree (top-down).
Combine with --json for a detailed nested JSON
dependency tree. Use --tree-json instead for a
simplified JSON dependency tree (requirement
strings as keys, dependencies as values), or
--tree-json-exact for exact pins as keys.
--tree-ascii Output human readable dependency tree with ASCII
tree markers.
--reversed-tree Output human readable dependency tree (bottom-up).
--reversed-tree-ascii Output human readable dependency tree (bottom-up)
with ASCII tree markers.
--max-depth INTEGER Maximum (JSON) tree rendering depth (default -1).
--cache-dir DIRECTORY Use a custom cache dir.
--no-cache-dir Disable pip cache for the wheels downloaded by
pipper. Overrides --cache-dir.
--index-url TEXT Base URL of the Python Package Index (default
https://pypi.org/simple).
--extra-index-url TEXT Extra URLs of package indexes to use in addition
to --index-url.
--threads INTEGER Maximum amount of threads to use for running
concurrent pip subprocesses.
--pre Include pre-release and development versions. By
default, pip implicitly excludes pre-releases
(unless specified otherwise by PEP 440).
-v, --verbose Control verbosity: -v will print cyclic
dependencies (WARNING), -vv will show solving
decisions (INFO), -vvv for development (DEBUG).
--skip-invalid-input Skip invalid requirements (e.g. internal
repositories, typos) and continue processing other
dependencies.
--version Show the version and exit.
-h, --help Show this message and exit.
Dependency trees
Exhaustive dependency trees without the need to install any packages (at most build some wheels).
$ pipgrip --tree pipgrip
pipgrip (0.12.0)
├── anytree>=2.4.1 (2.13.0)
├── click>=7 (8.3.1)
├── packaging>=17 (26.0)
├── pip>=22.2 (26.0)
├── setuptools<81,>=38.3 (80.10.2)
└── wheel (0.46.3)
└── packaging>=24.0 (26.0)
For more details/further processing, combine --tree with --json for a detailed nested JSON dependency tree. See also --tree-ascii (no unicode tree markers), and --tree-json & --tree-json-exact (simplified JSON dependency trees).
Lockfile generation
Using the --lock option, resolved (pinned) dependencies are additionally written to ./pipgrip.lock.
$ pipgrip --tree --lock botocore==1.13.48 'boto3>=1.10,<1.10.50'
botocore==1.13.48 (1.13.48)
├── docutils<0.16,>=0.10 (0.15.2)
├── jmespath<1.0.0,>=0.7.1 (0.9.5)
├── python-dateutil<3.0.0,>=2.1 (2.8.1)
│ └── six>=1.5 (1.14.0)
└── urllib3<1.26,>=1.20 (1.25.8)
boto3<1.10.50,>=1.10 (1.10.48)
├── botocore<1.14.0,>=1.13.48 (1.13.48)
│ ├── docutils<0.16,>=0.10 (0.15.2)
│ ├── jmespath<1.0.0,>=0.7.1 (0.9.5)
│ ├── python-dateutil<3.0.0,>=2.1 (2.8.1)
│ │ └── six>=1.5 (1.14.0)
│ └── urllib3<1.26,>=1.20 (1.25.8)
├── jmespath<1.0.0,>=0.7.1 (0.9.5)
└── s3transfer<0.3.0,>=0.2.0 (0.2.1)
└── botocore<2.0.0,>=1.12.36 (1.13.48)
├── docutils<0.16,>=0.10 (0.15.2)
├── jmespath<1.0.0,>=0.7.1 (0.9.5)
├── python-dateutil<3.0.0,>=2.1 (2.8.1)
│ └── six>=1.5 (1.14.0)
└── urllib3<1.26,>=1.20 (1.25.8)
$ cat ./pipgrip.lock
botocore==1.13.48
docutils==0.15.2
jmespath==0.9.5
python-dateutil==2.8.1
six==1.14.0
urllib3==1.25.8
boto3==1.10.48
s3transfer==0.2.1
NOTE: Since the selected botocore version is older than the one required by the recent versions of boto3, all boto3 versions will be checked for compatibility with botocore==1.13.48.
Version conflicts
If version conflicts exist for the given (ranges of) package version(s), a verbose explanation is raised.
$ pipgrip auto-sklearn~=0.6 dragnet==2.0.4
Error: Because dragnet (2.0.4) depends on scikit-learn (>=0.15.2,<0.21.0)
and auto-sklearn (0.6.0) depends on scikit-learn (<0.22,>=0.21.0), dragnet (2.0.4) is incompatible with auto-sklearn (0.6.0).
And because no versions of auto-sklearn match >0.6.0,<1.0, dragnet (2.0.4) is incompatible with auto-sklearn (>=0.6.0,<1.0).
So, because root depends on both auto-sklearn (~=0.6) and dragnet (==2.0.4), version solving failed.
NOTE: If older versions of auto-sklearn are allowed, PubGrub will try all acceptable versions of auto-sklearn. In this case, auto-sklearn==0.5.2 requires scikit-learn (<0.20,>=0.19), making it compatible with dragnet==2.0.4.
Cyclic dependencies
If cyclic dependencies are found, it is noted in the resulting tree.
$ pipgrip --tree -v keras==2.2.2
WARNING: Cyclic dependency found: keras depends on keras-applications and vice versa.
WARNING: Cyclic dependency found: keras depends on keras-preprocessing and vice versa.
keras==2.2.2 (2.2.2)
├── h5py (2.10.0)
│ ├── numpy>=1.7 (1.18.1)
│ └── six (1.14.0)
├── keras-applications==1.0.4 (1.0.4)
│ ├── h5py (2.10.0)
│ │ ├── numpy>=1.7 (1.18.1)
│ │ └── six (1.14.0)
│ ├── keras>=2.1.6 (2.2.2, cyclic)
│ └── numpy>=1.9.1 (1.18.1)
├── keras-preprocessing==1.0.2 (1.0.2)
│ ├── keras>=2.1.6 (2.2.2, cyclic)
│ ├── numpy>=1.9.1 (1.18.1)
│ ├── scipy>=0.14 (1.4.1)
│ │ └── numpy>=1.13.3 (1.18.1)
│ └── six>=1.9.0 (1.14.0)
├── numpy>=1.9.1 (1.18.1)
├── pyyaml (5.3)
├── scipy>=0.14 (1.4.1)
│ └── numpy>=1.13.3 (1.18.1)
└── six>=1.9.0 (1.14.0)
Known caveats
- PubGrub doesn't support version epochs, the main reason PyPA chose resolvelib over PubGrub for their new resolver.
- Package names are canonicalised in wheel metadata, resulting in e.g.
path.py -> path-pyandkeras_preprocessing -> keras-preprocessingin output. - VCS Support: combining VCS requirements with
--editable, as well as the@ -e svn+pattern are not supported. - Similar to setuptools'
install_requires, omitting theprojectname @prefix is not supported neither for VCS requirements (likepip install git+https...), nor for PEP 440 direct references (likepip install https...). - Parsing requirements files (
-r) does not support: custom file encodings, line continuations, global/per-requirement options --reversed-treeisn't implemented yet.- Since
pip install -rdoes not accept.as requirement, it is omitted from--lockoutput. So when installing local projects, either--pipeor--installshould be used (the latter basically doespipgrip --lock . && pip install . --constraint ./pipgrip.lock). - Local paths are not supported (like
pip install -e ../aiobotocore[boto3]), except for the current directory (likepipgrip --install -e .[boto3]).
Development
Run make help for options like installing for development, linting and testing.
See also
BSD 3-Clause License
Copyright (c) 2020 - 2024, ddelange, ddelange@delange.dev
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SPDX-License-Identifier: BSD-3-Clause
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pipgrip-0.12.0.tar.gz.
File metadata
- Download URL: pipgrip-0.12.0.tar.gz
- Upload date:
- Size: 54.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ededa399a5cc9f98c84155945a59faf3b20e49639586a8d181a539c3ca88acca
|
|
| MD5 |
9630e520d38b586f9f55230265b33792
|
|
| BLAKE2b-256 |
9401e58522e56df8f4b977202b60284d9963195a9380e81b0894612173be746d
|
Provenance
The following attestation bundles were made for pipgrip-0.12.0.tar.gz:
Publisher:
main.yml on ddelange/pipgrip
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipgrip-0.12.0.tar.gz -
Subject digest:
ededa399a5cc9f98c84155945a59faf3b20e49639586a8d181a539c3ca88acca - Sigstore transparency entry: 909529171
- Sigstore integration time:
-
Permalink:
ddelange/pipgrip@73578096d79c157e897dab7b8d39547f67ba601e -
Branch / Tag:
refs/tags/0.12.0 - Owner: https://github.com/ddelange
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yml@73578096d79c157e897dab7b8d39547f67ba601e -
Trigger Event:
release
-
Statement type:
File details
Details for the file pipgrip-0.12.0-py2.py3-none-any.whl.
File metadata
- Download URL: pipgrip-0.12.0-py2.py3-none-any.whl
- Upload date:
- Size: 82.8 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fae31bf959540ac8a5cd462a2bd8dd5e59ee43cc985d3e320d5cdc0ab8ca4d38
|
|
| MD5 |
175db333e4e0f4d17f429a47f308f4ce
|
|
| BLAKE2b-256 |
55aa5d386251652f2dab5d10b2ce83dd14a6a0c747ae75e2c4a2f25da8e99df1
|
Provenance
The following attestation bundles were made for pipgrip-0.12.0-py2.py3-none-any.whl:
Publisher:
main.yml on ddelange/pipgrip
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipgrip-0.12.0-py2.py3-none-any.whl -
Subject digest:
fae31bf959540ac8a5cd462a2bd8dd5e59ee43cc985d3e320d5cdc0ab8ca4d38 - Sigstore transparency entry: 909529173
- Sigstore integration time:
-
Permalink:
ddelange/pipgrip@73578096d79c157e897dab7b8d39547f67ba601e -
Branch / Tag:
refs/tags/0.12.0 - Owner: https://github.com/ddelange
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
main.yml@73578096d79c157e897dab7b8d39547f67ba601e -
Trigger Event:
release
-
Statement type: