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
.