This is a modern approach to defining python project dependencies, providing very precise, complete dependency graphs for a python project.
Find all files named Pipfile.lock
We parse Pipfile.lock
-- a json-structured file -- to find:
sources
- repositories/locations that can be referenced by packagesdefault
- production dependenciesdevelop
- development dependencies
Dependencies may contain an index
field -- this is a reference to a repository
in the top-level sources
.
Dependencies contain pinned version
information.
When possible, we use pipenv graph --json-tree
to hydrate the edges between
dependencies. This will fail unless pipenv install
has been run in that directory.
-
Pipfile.lock does not report edges, if
pipenv graph --json-tree
fails, we will not be able to detect the edges between dependencies.pipenv graph --json-tree
relies onpipenv install
being run in the directory beforehand. If that command was not run (such as in a freshly cloned repo), then the graph command will fail, which means that pipenv analysis will succeed, but with incomplete data (missing edges)- The graph command will also fail if
pipenv
is not installed or on the PATH.
Pipenv does a lot of work behind the scenes, so even a trivial example is quite large.
Pipfile
(created by running pipenv install requests && pipenv install --dev pytest
):
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
requests = "*"
[dev-packages]
pytest = "*"
Pipfile.lock
(created at the same time as Pipfile
above):
This file is an example, and may not represent the exact versions you would find if you ran this example yourself.
{
"_meta": {
"hash": {
"sha256": "03f05e808d849011739499ce042685cadfd6f14f4c6784ee3e6ad577b656437a"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2021.5.30"
},
"chardet": {
"hashes": [
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"version": "==4.0.0"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"version": "==2.10"
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
],
"index": "pypi",
"version": "==2.25.1"
},
"urllib3": {
"hashes": [
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
],
"version": "==1.26.6"
}
},
"develop": {
"attrs": {
"hashes": [
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"version": "==21.2.0"
},
"importlib-metadata": {
"hashes": [
"sha256:4a5611fea3768d3d967c447ab4e93f567d95db92225b43b7b238dbfb855d70bb",
"sha256:c6513572926a96458f8c8f725bf0e00108fba0c9583ade9bd15b869c9d726e33"
],
"markers": "python_version < '3.8'",
"version": "==4.6.0"
},
"iniconfig": {
"hashes": [
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
],
"version": "==1.1.1"
},
"packaging": {
"hashes": [
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
],
"version": "==20.9"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"version": "==0.13.1"
},
"py": {
"hashes": [
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
],
"version": "==1.10.0"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"version": "==2.4.7"
},
"pytest": {
"hashes": [
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
],
"index": "pypi",
"version": "==6.2.4"
},
"toml": {
"hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"version": "==0.10.2"
},
"typing-extensions": {
"hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
],
"markers": "python_version < '3.8'",
"version": "==3.10.0.0"
},
"zipp": {
"hashes": [
"sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
"sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
],
"version": "==3.4.1"
}
}
}
Output of pipenv graph --json-tree
:
[
{
"key": "pytest",
"package_name": "pytest",
"installed_version": "6.2.4",
"required_version": "6.2.4",
"dependencies": [
{
"key": "attrs",
"package_name": "attrs",
"installed_version": "21.2.0",
"required_version": ">=19.2.0",
"dependencies": []
},
{
"key": "importlib-metadata",
"package_name": "importlib-metadata",
"installed_version": "4.6.0",
"required_version": ">=0.12",
"dependencies": [
{
"key": "typing-extensions",
"package_name": "typing-extensions",
"installed_version": "3.10.0.0",
"required_version": ">=3.6.4",
"dependencies": []
},
{
"key": "zipp",
"package_name": "zipp",
"installed_version": "3.4.1",
"required_version": ">=0.5",
"dependencies": []
}
]
},
{
"key": "iniconfig",
"package_name": "iniconfig",
"installed_version": "1.1.1",
"required_version": "Any",
"dependencies": []
},
{
"key": "pluggy",
"package_name": "pluggy",
"installed_version": "0.13.1",
"required_version": ">=0.12,<1.0.0a1",
"dependencies": [
{
"key": "importlib-metadata",
"package_name": "importlib-metadata",
"installed_version": "4.6.0",
"required_version": ">=0.12",
"dependencies": [
{
"key": "typing-extensions",
"package_name": "typing-extensions",
"installed_version": "3.10.0.0",
"required_version": ">=3.6.4",
"dependencies": []
},
{
"key": "zipp",
"package_name": "zipp",
"installed_version": "3.4.1",
"required_version": ">=0.5",
"dependencies": []
}
]
}
]
},
{
"key": "py",
"package_name": "py",
"installed_version": "1.10.0",
"required_version": ">=1.8.2",
"dependencies": []
},
{
"key": "toml",
"package_name": "toml",
"installed_version": "0.10.2",
"required_version": "Any",
"dependencies": []
}
]
},
{
"key": "requests",
"package_name": "requests",
"installed_version": "2.25.1",
"required_version": "2.25.1",
"dependencies": [
{
"key": "certifi",
"package_name": "certifi",
"installed_version": "2021.5.30",
"required_version": ">=2017.4.17",
"dependencies": []
},
{
"key": "chardet",
"package_name": "chardet",
"installed_version": "4.0.0",
"required_version": ">=3.0.2,<5",
"dependencies": []
},
{
"key": "idna",
"package_name": "idna",
"installed_version": "2.10",
"required_version": ">=2.5,<3",
"dependencies": []
},
{
"key": "urllib3",
"package_name": "urllib3",
"installed_version": "1.26.6",
"required_version": ">=1.21.1,<1.27",
"dependencies": []
}
]
}
]
Final graph from our analysis:
Note that request and all of its dependencies are marked as production dependencies, while pytest and all of its dependencies
are marked as development dependencies (since pipenv
makes no distinction about dev/test).
The versions are omitted here, but are tracked as part of the graph as well.