tox-dev / pipdeptree

A command line utility to display dependency tree of the installed Python packages

Home Page:https://pypi.python.org/pypi/pipdeptree

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Graph stability: random order in Graphiz content

kdeldycke opened this issue · comments

In the same environment and the same set of dependency, the output generated by pipdeptree is not the same.

To test this, run in succession the same pipdeptree call:

$ pipdeptree --packages click-extra --graph-output dot > graph1.dot
$ pipdeptree --packages click-extra --graph-output dot > graph2.dot
$ pipdeptree --packages click-extra --graph-output dot > graph3.dot

You can check none of the produced files are identical:

❯ shasum graph*.dot
302894f16f812b6e360e2360198f90d1d187c6a9  graph1.dot
8e8de92b7eed4d95d7385a53e3ab570ce8101bf5  graph2.dot
c283579a78573ae431961dc19aec09d752c8cabf  graph3.dot

That's because the edges produced in the output are not sorted and randomly ordered at each pipdeptree invokation:

--- ./graph1.dot	2023-01-09 15:46:19
+++ ./graph2.dot	2023-01-09 15:46:23
@@ -1,85 +1,85 @@
 digraph {
 	"click-extra" [label="click-extra\n3.8.0"]
-	"click-extra" -> xmltodict [label=">=0.12,<0.14"]
-	"click-extra" -> commentjson [label=">=0.9.0,<0.10.0"]
-	"click-extra" -> regex [label=">=2022.3.15,<2023.0.0"]
+	"click-extra" -> mergedeep [label=">=1.3.4,<2.0.0"]
 	"click-extra" -> cloup [label=">=2.0.0.post1,<3.0.0"]
-	"click-extra" -> wcmatch [label=">=8.4,<9.0"]
-	"click-extra" -> sphinx [label=">=5.3.0,<6.0.0"]
-	"click-extra" -> "pygments-ansi-color" [label=">=0.0.6,<0.1.1"]
-	"click-extra" -> pygments [label=">=2.14.0,<3.0.0"]
 	"click-extra" -> click [label=">=8.1.1,<9.0.0"]
-	"click-extra" -> "pallets-sphinx-themes" [label=">=2.0.2,<3.0.0"]
+	"click-extra" -> pygments [label=">=2.14.0,<3.0.0"]
+	"click-extra" -> "pygments-ansi-color" [label=">=0.0.6,<0.1.1"]
 	"click-extra" -> requests [label=">=2.27.1,<3.0.0"]
-	"click-extra" -> mergedeep [label=">=1.3.4,<2.0.0"]
-	"click-extra" -> pyyaml [label=">=6.0.0,<7.0.0"]
+	"click-extra" -> sphinx [label=">=5.3.0,<6.0.0"]
 	"click-extra" -> tabulate [label=">=0.9.0,<0.10.0"]
-	"click-extra" -> boltons [label=">=21.0.0,<22.0.0"]
+	"click-extra" -> xmltodict [label=">=0.12,<0.14"]
+	"click-extra" -> regex [label=">=2022.3.15,<2023.0.0"]
+	"click-extra" -> commentjson [label=">=0.9.0,<0.10.0"]
 	"click-extra" -> "click-log" [label=">=0.4.0,<0.5.0"]
-	"click-log" [label="click-log\n0.4.0"]
-	"click-log" -> click [label=any]
-	click [label="click\n8.1.3"]
-	boltons [label="boltons\n21.0.0"]
-	tabulate [label="tabulate\n0.9.0"]
-	pyyaml [label="PyYAML\n6.0"]
-	mergedeep [label="mergedeep\n1.3.4"]
-	requests [label="requests\n2.28.1"]
-	requests -> idna [label=">=2.5,<4"]
-	requests -> urllib3 [label=">=1.21.1,<1.27"]
-	requests -> "charset-normalizer" [label=">=2,<3"]
-	requests -> certifi [label=">=2017.4.17"]
-	certifi [label="certifi\n2022.12.7"]
-	"charset-normalizer" [label="charset-normalizer\n2.1.1"]
-	urllib3 [label="urllib3\n1.26.13"]
-	idna [label="idna\n3.4"]
+	"click-extra" -> pyyaml [label=">=6.0.0,<7.0.0"]
+	"click-extra" -> wcmatch [label=">=8.4,<9.0"]
+	"click-extra" -> boltons [label=">=21.0.0,<22.0.0"]
+	"click-extra" -> "pallets-sphinx-themes" [label=">=2.0.2,<3.0.0"]
 	"pallets-sphinx-themes" [label="Pallets-Sphinx-Themes\n2.0.3"]
-	"pallets-sphinx-themes" -> sphinx [label=any]
 	"pallets-sphinx-themes" -> packaging [label=any]
-	packaging [label="packaging\n23.0"]
+	"pallets-sphinx-themes" -> sphinx [label=any]
 	sphinx [label="sphinx\n5.3.0"]
+	sphinx -> docutils [label=">=0.14,<0.20"]
+	sphinx -> "sphinxcontrib-jsmath" [label=any]
+	sphinx -> babel [label=">=2.9"]
 	sphinx -> alabaster [label=">=0.7,<0.8"]
-	sphinx -> pygments [label=">=2.12"]
-	sphinx -> "sphinxcontrib-qthelp" [label=any]
 	sphinx -> snowballstemmer [label=">=2.0"]
-	sphinx -> babel [label=">=2.9"]
-	sphinx -> "sphinxcontrib-serializinghtml" [label=">=1.1.5"]
+	sphinx -> requests [label=">=2.5.0"]
+	sphinx -> pygments [label=">=2.12"]
 	sphinx -> packaging [label=">=21.0"]
 	sphinx -> "sphinxcontrib-applehelp" [label=any]
-	sphinx -> imagesize [label=">=1.3"]
+	sphinx -> "sphinxcontrib-serializinghtml" [label=">=1.1.5"]
 	sphinx -> jinja2 [label=">=3.0"]
-	sphinx -> "sphinxcontrib-htmlhelp" [label=">=2.0.0"]
-	sphinx -> requests [label=">=2.5.0"]
-	sphinx -> docutils [label=">=0.14,<0.20"]
+	sphinx -> imagesize [label=">=1.3"]
 	sphinx -> "sphinxcontrib-devhelp" [label=any]
-	sphinx -> "sphinxcontrib-jsmath" [label=any]
-	"sphinxcontrib-jsmath" [label="sphinxcontrib-jsmath\n1.0.1"]
-	"sphinxcontrib-devhelp" [label="sphinxcontrib-devhelp\n1.0.2"]
-	docutils [label="docutils\n0.19"]
+	sphinx -> "sphinxcontrib-qthelp" [label=any]
+	sphinx -> "sphinxcontrib-htmlhelp" [label=">=2.0.0"]
 	"sphinxcontrib-htmlhelp" [label="sphinxcontrib-htmlhelp\n2.0.0"]
+	"sphinxcontrib-qthelp" [label="sphinxcontrib-qthelp\n1.0.3"]
+	"sphinxcontrib-devhelp" [label="sphinxcontrib-devhelp\n1.0.2"]
+	imagesize [label="imagesize\n1.4.1"]
 	jinja2 [label="Jinja2\n3.1.2"]
 	jinja2 -> markupsafe [label=">=2.0"]
 	markupsafe [label="MarkupSafe\n2.1.1"]
-	imagesize [label="imagesize\n1.4.1"]
-	"sphinxcontrib-applehelp" [label="sphinxcontrib-applehelp\n1.0.2"]
 	"sphinxcontrib-serializinghtml" [label="sphinxcontrib-serializinghtml\n1.1.5"]
+	"sphinxcontrib-applehelp" [label="sphinxcontrib-applehelp\n1.0.2"]
+	packaging [label="packaging\n23.0"]
+	pygments [label="Pygments\n2.14.0"]
+	requests [label="requests\n2.28.1"]
+	requests -> urllib3 [label=">=1.21.1,<1.27"]
+	requests -> certifi [label=">=2017.4.17"]
+	requests -> "charset-normalizer" [label=">=2,<3"]
+	requests -> idna [label=">=2.5,<4"]
+	idna [label="idna\n3.4"]
+	"charset-normalizer" [label="charset-normalizer\n2.1.1"]
+	certifi [label="certifi\n2022.12.7"]
+	urllib3 [label="urllib3\n1.26.13"]
+	snowballstemmer [label="snowballstemmer\n2.2.0"]
+	alabaster [label="alabaster\n0.7.12"]
 	babel [label="Babel\n2.11.0"]
 	babel -> pytz [label=">=2015.7"]
 	pytz [label="pytz\n2022.7"]
-	snowballstemmer [label="snowballstemmer\n2.2.0"]
-	"sphinxcontrib-qthelp" [label="sphinxcontrib-qthelp\n1.0.3"]
-	pygments [label="Pygments\n2.14.0"]
-	alabaster [label="alabaster\n0.7.12"]
-	"pygments-ansi-color" [label="pygments-ansi-color\n0.1.0"]
-	"pygments-ansi-color" -> pygments [label="!=2.7.3"]
+	"sphinxcontrib-jsmath" [label="sphinxcontrib-jsmath\n1.0.1"]
+	docutils [label="docutils\n0.19"]
+	boltons [label="boltons\n21.0.0"]
 	wcmatch [label="wcmatch\n8.4.1"]
 	wcmatch -> bracex [label=">=2.1.1"]
 	bracex [label="bracex\n2.3.post1"]
-	cloup [label="cloup\n2.0.0.post1"]
-	cloup -> click [label=">=8.0,<9.0"]
-	regex [label="regex\n2022.10.31"]
+	pyyaml [label="PyYAML\n6.0"]
+	"click-log" [label="click-log\n0.4.0"]
+	"click-log" -> click [label=any]
+	click [label="click\n8.1.3"]
 	commentjson [label="commentjson\n0.9.0"]
 	commentjson -> "lark-parser" [label=">=0.7.1,<0.8.0"]
 	"lark-parser" [label="lark-parser\n0.7.8"]
+	regex [label="regex\n2022.10.31"]
 	xmltodict [label="xmltodict\n0.13.0"]
+	tabulate [label="tabulate\n0.9.0"]
+	"pygments-ansi-color" [label="pygments-ansi-color\n0.1.0"]
+	"pygments-ansi-color" -> pygments [label="!=2.7.3"]
+	cloup [label="cloup\n2.0.0.post1"]
+	cloup -> click [label=">=8.0,<9.0"]
+	mergedeep [label="mergedeep\n1.3.4"]
 }

The solution would be to sort out the content of the the Graphiz nodes and edges before the output.

FYI, I proposed a fixed in this PR: #189

For the record, this has been implemented in pipdeptree v2.4.0.