Thu, 20 Jul 2023 11:00:30 +0200
Removed support for Python 3.7 because that is EOL.
9218 | 1 | #!/usr/bin/env python |
2 | # -*- coding: utf-8 -*- | |
3 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
4 | # |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
5 | # eric-ide modification: Copyright from file "LICENSE" |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
6 | # |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
7 | |
9218 | 8 | """ |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
9 | Copyright (c) The pipdeptree developers |
9218 | 10 | |
11 | Permission is hereby granted, free of charge, to any person obtaining | |
12 | a copy of this software and associated documentation files (the | |
13 | "Software"), to deal in the Software without restriction, including | |
14 | without limitation the rights to use, copy, modify, merge, publish, | |
15 | distribute, sublicense, and/or sell copies of the Software, and to | |
16 | permit persons to whom the Software is furnished to do so, subject to | |
17 | the following conditions: | |
18 | ||
19 | The above copyright notice and this permission notice shall be | |
20 | included in all copies or substantial portions of the Software. | |
21 | ||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
26 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
27 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
28 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
29 | """ | |
30 | ||
31 | # | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
32 | # Slightly modified to be used within the eric-ide project. |
9218 | 33 | # |
9653
e67609152c5e
Updated copyright for 2023.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9589
diff
changeset
|
34 | # Copyright (c) 2022 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> |
9218 | 35 | # |
36 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
37 | import argparse |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
38 | import fnmatch |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
39 | import inspect |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
40 | import json |
9218 | 41 | import os |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
42 | import shutil |
9218 | 43 | import subprocess |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
44 | import sys |
9218 | 45 | import tempfile |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
46 | from collections import defaultdict, deque |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
47 | from collections.abc import Mapping |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
48 | from importlib import import_module |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
49 | from itertools import chain |
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
50 | from textwrap import dedent |
9218 | 51 | |
52 | from pip._vendor import pkg_resources | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
53 | |
9218 | 54 | try: |
55 | from pip._internal.operations.freeze import FrozenRequirement | |
56 | except ImportError: | |
57 | from pip import FrozenRequirement | |
58 | ||
59 | ||
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
60 | __version__ = '2.7.1' |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
61 | __version_tuple__ = (2, 7, 1) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
62 | # eric-ide modification: from version.py |
9218 | 63 | |
64 | flatten = chain.from_iterable | |
65 | ||
66 | ||
67 | def sorted_tree(tree): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
68 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
69 | Sorts the dict representation of the tree. The root packages as well as the intermediate packages are sorted in the |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
70 | alphabetical order of the package names. |
9218 | 71 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
72 | :param dict tree: the pkg dependency tree obtained by calling `construct_tree` function |
9218 | 73 | :returns: sorted tree |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
74 | :rtype: dict |
9218 | 75 | """ |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
76 | return {k: sorted(v) for k, v in sorted(tree.items())} |
9218 | 77 | |
78 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
79 | def guess_version(pkg_key, default="?"): |
9218 | 80 | """Guess the version of a pkg when pip doesn't provide it |
81 | ||
82 | :param str pkg_key: key of the package | |
83 | :param str default: default version to return if unable to find | |
84 | :returns: version | |
85 | :rtype: string | |
86 | """ | |
87 | try: | |
10119
64147a7e6393
Removed support for Python 3.7 because that is EOL.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10026
diff
changeset
|
88 | import importlib.metadata |
64147a7e6393
Removed support for Python 3.7 because that is EOL.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10026
diff
changeset
|
89 | return importlib.metadata.version(pkg_key) |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
90 | except ImportError: |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
91 | pass |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
92 | # Avoid AssertionError with setuptools, see https://github.com/tox-dev/pipdeptree/issues/162 |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
93 | if pkg_key in {"setuptools"}: |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
94 | return default |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
95 | try: |
9218 | 96 | m = import_module(pkg_key) |
97 | except ImportError: | |
98 | return default | |
99 | else: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
100 | v = getattr(m, "__version__", default) |
9218 | 101 | if inspect.ismodule(v): |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
102 | return getattr(v, "__version__", default) |
9218 | 103 | else: |
104 | return v | |
105 | ||
106 | ||
107 | def frozen_req_from_dist(dist): | |
108 | # The `pip._internal.metadata` modules were introduced in 21.1.1 | |
109 | # and the `pip._internal.operations.freeze.FrozenRequirement` | |
110 | # class now expects dist to be a subclass of | |
111 | # `pip._internal.metadata.BaseDistribution`, however the | |
112 | # `pip._internal.utils.misc.get_installed_distributions` continues | |
113 | # to return objects of type | |
114 | # pip._vendor.pkg_resources.DistInfoDistribution. | |
115 | # | |
116 | # This is a hacky backward compatible (with older versions of pip) | |
117 | # fix. | |
118 | try: | |
119 | from pip._internal import metadata | |
120 | except ImportError: | |
121 | pass | |
122 | else: | |
123 | dist = metadata.pkg_resources.Distribution(dist) | |
124 | ||
125 | try: | |
126 | return FrozenRequirement.from_dist(dist) | |
127 | except TypeError: | |
128 | return FrozenRequirement.from_dist(dist, []) | |
129 | ||
130 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
131 | class Package: |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
132 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
133 | Abstract class for wrappers around objects that pip returns. This class needs to be subclassed with implementations |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
134 | for `render_as_root` and `render_as_branch` methods. |
9218 | 135 | """ |
136 | ||
137 | def __init__(self, obj): | |
138 | self._obj = obj | |
139 | self.project_name = obj.project_name | |
140 | self.key = obj.key | |
141 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
142 | def render_as_root(self, frozen): # noqa: U100 |
9218 | 143 | return NotImplementedError |
144 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
145 | def render_as_branch(self, frozen): # noqa: U100 |
9218 | 146 | return NotImplementedError |
147 | ||
148 | def render(self, parent=None, frozen=False): | |
149 | if not parent: | |
150 | return self.render_as_root(frozen) | |
151 | else: | |
152 | return self.render_as_branch(frozen) | |
153 | ||
154 | @staticmethod | |
155 | def frozen_repr(obj): | |
156 | fr = frozen_req_from_dist(obj) | |
157 | return str(fr).strip() | |
158 | ||
159 | def __getattr__(self, key): | |
160 | return getattr(self._obj, key) | |
161 | ||
162 | def __repr__(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
163 | return f'<{self.__class__.__name__}("{self.key}")>' |
9218 | 164 | |
165 | def __lt__(self, rhs): | |
166 | return self.key < rhs.key | |
167 | ||
168 | ||
169 | class DistPackage(Package): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
170 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
171 | Wrapper class for pkg_resources.Distribution instances |
9218 | 172 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
173 | :param obj: pkg_resources.Distribution to wrap over |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
174 | :param req: optional ReqPackage object to associate this DistPackage with. This is useful for displaying the tree |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
175 | in reverse |
9218 | 176 | """ |
177 | ||
178 | def __init__(self, obj, req=None): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
179 | super().__init__(obj) |
9218 | 180 | self.version_spec = None |
181 | self.req = req | |
182 | ||
183 | def render_as_root(self, frozen): | |
184 | if not frozen: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
185 | return f"{self.project_name}=={self.version}" |
9218 | 186 | else: |
187 | return self.__class__.frozen_repr(self._obj) | |
188 | ||
189 | def render_as_branch(self, frozen): | |
190 | assert self.req is not None | |
191 | if not frozen: | |
192 | parent_ver_spec = self.req.version_spec | |
193 | parent_str = self.req.project_name | |
194 | if parent_ver_spec: | |
195 | parent_str += parent_ver_spec | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
196 | return f"{self.project_name}=={self.version} [requires: {parent_str}]" |
9218 | 197 | else: |
198 | return self.render_as_root(frozen) | |
199 | ||
200 | def as_requirement(self): | |
201 | """Return a ReqPackage representation of this DistPackage""" | |
202 | return ReqPackage(self._obj.as_requirement(), dist=self) | |
203 | ||
204 | def as_parent_of(self, req): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
205 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
206 | Return a DistPackage instance associated to a requirement. This association is necessary for reversing the |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
207 | PackageDAG. |
9218 | 208 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
209 | If `req` is None, and the `req` attribute of the current instance is also None, then the same instance will be |
9218 | 210 | returned. |
211 | ||
212 | :param ReqPackage req: the requirement to associate with | |
213 | :returns: DistPackage instance | |
214 | """ | |
215 | if req is None and self.req is None: | |
216 | return self | |
217 | return self.__class__(self._obj, req) | |
218 | ||
219 | def as_dict(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
220 | return {"key": self.key, "package_name": self.project_name, "installed_version": self.version} |
9218 | 221 | |
222 | ||
223 | class ReqPackage(Package): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
224 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
225 | Wrapper class for Requirements instance |
9218 | 226 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
227 | :param obj: The `Requirements` instance to wrap over |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
228 | :param dist: optional `pkg_resources.Distribution` instance for this requirement |
9218 | 229 | """ |
230 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
231 | UNKNOWN_VERSION = "?" |
9218 | 232 | |
233 | def __init__(self, obj, dist=None): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
234 | super().__init__(obj) |
9218 | 235 | self.dist = dist |
236 | ||
237 | @property | |
238 | def version_spec(self): | |
239 | specs = sorted(self._obj.specs, reverse=True) # `reverse` makes '>' prior to '<' | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
240 | return ",".join(["".join(sp) for sp in specs]) if specs else None |
9218 | 241 | |
242 | @property | |
243 | def installed_version(self): | |
244 | if not self.dist: | |
245 | return guess_version(self.key, self.UNKNOWN_VERSION) | |
246 | return self.dist.version | |
247 | ||
248 | @property | |
249 | def is_missing(self): | |
250 | return self.installed_version == self.UNKNOWN_VERSION | |
251 | ||
252 | def is_conflicting(self): | |
253 | """If installed version conflicts with required version""" | |
254 | # unknown installed version is also considered conflicting | |
255 | if self.installed_version == self.UNKNOWN_VERSION: | |
256 | return True | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
257 | ver_spec = self.version_spec if self.version_spec else "" |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
258 | req_version_str = f"{self.project_name}{ver_spec}" |
9218 | 259 | req_obj = pkg_resources.Requirement.parse(req_version_str) |
260 | return self.installed_version not in req_obj | |
261 | ||
262 | def render_as_root(self, frozen): | |
263 | if not frozen: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
264 | return f"{self.project_name}=={self.installed_version}" |
9218 | 265 | elif self.dist: |
266 | return self.__class__.frozen_repr(self.dist._obj) | |
267 | else: | |
268 | return self.project_name | |
269 | ||
270 | def render_as_branch(self, frozen): | |
271 | if not frozen: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
272 | req_ver = self.version_spec if self.version_spec else "Any" |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
273 | return f"{self.project_name} [required: {req_ver}, installed: {self.installed_version}]" |
9218 | 274 | else: |
275 | return self.render_as_root(frozen) | |
276 | ||
277 | def as_dict(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
278 | return { |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
279 | "key": self.key, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
280 | "package_name": self.project_name, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
281 | "installed_version": self.installed_version, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
282 | "required_version": self.version_spec, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
283 | } |
9218 | 284 | |
285 | ||
286 | class PackageDAG(Mapping): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
287 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
288 | Representation of Package dependencies as directed acyclic graph using a dict (Mapping) as the underlying |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
289 | datastructure. |
9218 | 290 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
291 | The nodes and their relationships (edges) are internally stored using a map as follows, |
9218 | 292 | |
293 | {a: [b, c], | |
294 | b: [d], | |
295 | c: [d, e], | |
296 | d: [e], | |
297 | e: [], | |
298 | f: [b], | |
299 | g: [e, f]} | |
300 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
301 | Here, node `a` has 2 children nodes `b` and `c`. Consider edge direction from `a` -> `b` and `a` -> `c` |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
302 | respectively. |
9218 | 303 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
304 | A node is expected to be an instance of a subclass of `Package`. The keys are must be of class `DistPackage` and |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
305 | each item in values must be of class `ReqPackage`. (See also ReversedPackageDAG where the key and value types are |
9218 | 306 | interchanged). |
307 | """ | |
308 | ||
309 | @classmethod | |
310 | def from_pkgs(cls, pkgs): | |
311 | pkgs = [DistPackage(p) for p in pkgs] | |
312 | idx = {p.key: p for p in pkgs} | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
313 | m = {p: [ReqPackage(r, idx.get(r.key)) for r in p.requires()] for p in pkgs} |
9218 | 314 | return cls(m) |
315 | ||
316 | def __init__(self, m): | |
317 | """Initialize the PackageDAG object | |
318 | ||
319 | :param dict m: dict of node objects (refer class docstring) | |
320 | :returns: None | |
321 | :rtype: NoneType | |
322 | ||
323 | """ | |
324 | self._obj = m | |
325 | self._index = {p.key: p for p in list(self._obj)} | |
326 | ||
327 | def get_node_as_parent(self, node_key): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
328 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
329 | Get the node from the keys of the dict representing the DAG. |
9218 | 330 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
331 | This method is useful if the dict representing the DAG contains different kind of objects in keys and values. |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
332 | Use this method to look up a node obj as a parent (from the keys of the dict) given a node key. |
9218 | 333 | |
334 | :param node_key: identifier corresponding to key attr of node obj | |
335 | :returns: node obj (as present in the keys of the dict) | |
336 | :rtype: Object | |
337 | """ | |
338 | try: | |
339 | return self._index[node_key] | |
340 | except KeyError: | |
341 | return None | |
342 | ||
343 | def get_children(self, node_key): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
344 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
345 | Get child nodes for a node by its key |
9218 | 346 | |
347 | :param str node_key: key of the node to get children of | |
348 | :returns: list of child nodes | |
349 | :rtype: ReqPackage[] | |
350 | """ | |
351 | node = self.get_node_as_parent(node_key) | |
352 | return self._obj[node] if node else [] | |
353 | ||
354 | def filter(self, include, exclude): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
355 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
356 | Filters nodes in a graph by given parameters |
9218 | 357 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
358 | If a node is included, then all it's children are also included. |
9218 | 359 | |
360 | :param set include: set of node keys to include (or None) | |
361 | :param set exclude: set of node keys to exclude (or None) | |
362 | :returns: filtered version of the graph | |
363 | :rtype: PackageDAG | |
364 | """ | |
365 | # If neither of the filters are specified, short circuit | |
366 | if include is None and exclude is None: | |
367 | return self | |
368 | ||
369 | # Note: In following comparisons, we use lower cased values so | |
370 | # that user may specify `key` or `project_name`. As per the | |
371 | # documentation, `key` is simply | |
372 | # `project_name.lower()`. Refer: | |
373 | # https://setuptools.readthedocs.io/en/latest/pkg_resources.html#distribution-objects | |
374 | if include: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
375 | include = {s.lower() for s in include} |
9218 | 376 | if exclude: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
377 | exclude = {s.lower() for s in exclude} |
9218 | 378 | else: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
379 | exclude = set() |
9218 | 380 | |
381 | # Check for mutual exclusion of show_only and exclude sets | |
382 | # after normalizing the values to lowercase | |
383 | if include and exclude: | |
384 | assert not (include & exclude) | |
385 | ||
386 | # Traverse the graph in a depth first manner and filter the | |
387 | # nodes according to `show_only` and `exclude` sets | |
388 | stack = deque() | |
389 | m = {} | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
390 | seen = set() |
9218 | 391 | for node in self._obj.keys(): |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
392 | if any(fnmatch.fnmatch(node.key, e) for e in exclude): |
9218 | 393 | continue |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
394 | if include is None or any(fnmatch.fnmatch(node.key, i) for i in include): |
9218 | 395 | stack.append(node) |
396 | while True: | |
397 | if len(stack) > 0: | |
398 | n = stack.pop() | |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
399 | cldn = [c for c in self._obj[n] if not any(fnmatch.fnmatch(c.key, e) for e in exclude)] |
9218 | 400 | m[n] = cldn |
401 | seen.add(n.key) | |
402 | for c in cldn: | |
403 | if c.key not in seen: | |
404 | cld_node = self.get_node_as_parent(c.key) | |
405 | if cld_node: | |
406 | stack.append(cld_node) | |
407 | else: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
408 | # It means there's no root node corresponding to the child node i.e. |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
409 | # a dependency is missing |
9218 | 410 | continue |
411 | else: | |
412 | break | |
413 | ||
414 | return self.__class__(m) | |
415 | ||
416 | def reverse(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
417 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
418 | Reverse the DAG, or turn it upside-down. |
9218 | 419 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
420 | In other words, the directions of edges of the nodes in the DAG will be reversed. |
9218 | 421 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
422 | Note that this function purely works on the nodes in the graph. This implies that to perform a combination of |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
423 | filtering and reversing, the order in which `filter` and `reverse` methods should be applied is important. For |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
424 | e.g., if reverse is called on a filtered graph, then only the filtered nodes and it's children will be |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
425 | considered when reversing. On the other hand, if filter is called on reversed DAG, then the definition of |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
426 | "child" nodes is as per the reversed DAG. |
9218 | 427 | |
428 | :returns: DAG in the reversed form | |
429 | :rtype: ReversedPackageDAG | |
430 | """ | |
431 | m = defaultdict(list) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
432 | child_keys = {r.key for r in chain.from_iterable(self._obj.values())} |
9218 | 433 | for k, vs in self._obj.items(): |
434 | for v in vs: | |
435 | # if v is already added to the dict, then ensure that | |
436 | # we are using the same object. This check is required | |
437 | # as we're using array mutation | |
438 | try: | |
439 | node = [p for p in m.keys() if p.key == v.key][0] | |
440 | except IndexError: | |
441 | node = v | |
442 | m[node].append(k.as_parent_of(v)) | |
443 | if k.key not in child_keys: | |
444 | m[k.as_requirement()] = [] | |
445 | return ReversedPackageDAG(dict(m)) | |
446 | ||
447 | def sort(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
448 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
449 | Return sorted tree in which the underlying _obj dict is an dict, sorted alphabetically by the keys. |
9218 | 450 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
451 | :returns: Instance of same class with dict |
9218 | 452 | """ |
453 | return self.__class__(sorted_tree(self._obj)) | |
454 | ||
455 | # Methods required by the abstract base class Mapping | |
456 | def __getitem__(self, *args): | |
457 | return self._obj.get(*args) | |
458 | ||
459 | def __iter__(self): | |
460 | return self._obj.__iter__() | |
461 | ||
462 | def __len__(self): | |
463 | return len(self._obj) | |
464 | ||
465 | ||
466 | class ReversedPackageDAG(PackageDAG): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
467 | """Representation of Package dependencies in the reverse order. |
9218 | 468 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
469 | Similar to it's super class `PackageDAG`, the underlying datastructure is a dict, but here the keys are expected to |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
470 | be of type `ReqPackage` and each item in the values of type `DistPackage`. |
9218 | 471 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
472 | Typically, this object will be obtained by calling `PackageDAG.reverse`. |
9218 | 473 | """ |
474 | ||
475 | def reverse(self): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
476 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
477 | Reverse the already reversed DAG to get the PackageDAG again |
9218 | 478 | |
479 | :returns: reverse of the reversed DAG | |
480 | :rtype: PackageDAG | |
481 | """ | |
482 | m = defaultdict(list) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
483 | child_keys = {r.key for r in chain.from_iterable(self._obj.values())} |
9218 | 484 | for k, vs in self._obj.items(): |
485 | for v in vs: | |
486 | try: | |
487 | node = [p for p in m.keys() if p.key == v.key][0] | |
488 | except IndexError: | |
489 | node = v.as_parent_of(None) | |
490 | m[node].append(k) | |
491 | if k.key not in child_keys: | |
492 | m[k.dist] = [] | |
493 | return PackageDAG(dict(m)) | |
494 | ||
495 | ||
496 | def render_text(tree, list_all=True, frozen=False): | |
497 | """Print tree as text on console | |
498 | ||
499 | :param dict tree: the package tree | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
500 | :param bool list_all: whether to list all the pgks at the root level or only those that are the sub-dependencies |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
501 | :param bool frozen: show the names of the pkgs in the output that's favourable to pip --freeze |
9218 | 502 | :returns: None |
503 | ||
504 | """ | |
505 | tree = tree.sort() | |
506 | nodes = tree.keys() | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
507 | branch_keys = {r.key for r in chain.from_iterable(tree.values())} |
9218 | 508 | use_bullets = not frozen |
509 | ||
510 | if not list_all: | |
511 | nodes = [p for p in nodes if p.key not in branch_keys] | |
512 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
513 | def aux(node, parent=None, indent=0, cur_chain=None): |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
514 | cur_chain = cur_chain or [] |
9218 | 515 | node_str = node.render(parent, frozen) |
516 | if parent: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
517 | prefix = " " * indent + ("- " if use_bullets else "") |
9218 | 518 | node_str = prefix + node_str |
519 | result = [node_str] | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
520 | children = [ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
521 | aux(c, node, indent=indent + 2, cur_chain=cur_chain + [c.project_name]) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
522 | for c in tree.get_children(node.key) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
523 | if c.project_name not in cur_chain |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
524 | ] |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
525 | result += list(chain.from_iterable(children)) |
9218 | 526 | return result |
527 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
528 | lines = chain.from_iterable([aux(p) for p in nodes]) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
529 | print("\n".join(lines)) |
9218 | 530 | |
531 | ||
532 | def render_json(tree, indent): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
533 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
534 | Converts the tree into a flat json representation. |
9218 | 535 | |
536 | The json repr will be a list of hashes, each hash having 2 fields: | |
537 | - package | |
538 | - dependencies: list of dependencies | |
539 | ||
540 | :param dict tree: dependency tree | |
541 | :param int indent: no. of spaces to indent json | |
542 | :returns: json representation of the tree | |
543 | :rtype: str | |
544 | """ | |
545 | tree = tree.sort() | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
546 | return json.dumps( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
547 | [{"package": k.as_dict(), "dependencies": [v.as_dict() for v in vs]} for k, vs in tree.items()], indent=indent |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
548 | ) |
9218 | 549 | |
550 | ||
551 | def render_json_tree(tree, indent): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
552 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
553 | Converts the tree into a nested json representation. |
9218 | 554 | |
555 | The json repr will be a list of hashes, each hash having the following fields: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
556 | |
9218 | 557 | - package_name |
558 | - key | |
559 | - required_version | |
560 | - installed_version | |
561 | - dependencies: list of dependencies | |
562 | ||
563 | :param dict tree: dependency tree | |
564 | :param int indent: no. of spaces to indent json | |
565 | :returns: json representation of the tree | |
566 | :rtype: str | |
567 | """ | |
568 | tree = tree.sort() | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
569 | branch_keys = {r.key for r in chain.from_iterable(tree.values())} |
9218 | 570 | nodes = [p for p in tree.keys() if p.key not in branch_keys] |
571 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
572 | def aux(node, parent=None, cur_chain=None): |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
573 | if cur_chain is None: |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
574 | cur_chain = [node.project_name] |
9218 | 575 | |
576 | d = node.as_dict() | |
577 | if parent: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
578 | d["required_version"] = node.version_spec if node.version_spec else "Any" |
9218 | 579 | else: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
580 | d["required_version"] = d["installed_version"] |
9218 | 581 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
582 | d["dependencies"] = [ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
583 | aux(c, parent=node, cur_chain=cur_chain + [c.project_name]) |
9218 | 584 | for c in tree.get_children(node.key) |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
585 | if c.project_name not in cur_chain |
9218 | 586 | ] |
587 | ||
588 | return d | |
589 | ||
590 | return json.dumps([aux(p) for p in nodes], indent=indent) | |
591 | ||
592 | ||
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
593 | def render_mermaid(tree) -> str: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
594 | """Produce a Mermaid flowchart from the dependency graph. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
595 | |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
596 | :param dict tree: dependency graph |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
597 | """ |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
598 | # List of reserved keywords in Mermaid that cannot be used as node names. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
599 | # See: https://github.com/mermaid-js/mermaid/issues/4182#issuecomment-1454787806 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
600 | reserved_ids: set[str] = { |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
601 | "C4Component", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
602 | "C4Container", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
603 | "C4Deployment", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
604 | "C4Dynamic", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
605 | "_blank", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
606 | "_parent", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
607 | "_self", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
608 | "_top", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
609 | "call", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
610 | "class", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
611 | "classDef", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
612 | "click", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
613 | "end", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
614 | "flowchart", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
615 | "flowchart-v2", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
616 | "graph", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
617 | "interpolate", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
618 | "linkStyle", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
619 | "style", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
620 | "subgraph", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
621 | } |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
622 | node_ids_map: dict[str:str] = {} |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
623 | |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
624 | def mermaid_id(key: str) -> str: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
625 | """Returns a valid Mermaid node ID from a string.""" |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
626 | # If we have already seen this key, return the canonical ID. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
627 | canonical_id = node_ids_map.get(key) |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
628 | if canonical_id is not None: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
629 | return canonical_id |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
630 | # If the key is not a reserved keyword, return it as is, and update the map. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
631 | if key not in reserved_ids: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
632 | node_ids_map[key] = key |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
633 | return key |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
634 | # If the key is a reserved keyword, append a number to it. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
635 | number = 0 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
636 | while True: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
637 | new_id = f"{key}_{number}" |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
638 | if new_id not in node_ids_map: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
639 | node_ids_map[key] = new_id |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
640 | return new_id |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
641 | number += 1 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
642 | |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
643 | # Use a sets to avoid duplicate entries. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
644 | nodes: set[str] = set() |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
645 | edges: set[str] = set() |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
646 | |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
647 | if isinstance(tree, ReversedPackageDAG): |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
648 | for package, reverse_dependencies in tree.items(): |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
649 | package_label = "\\n".join( |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
650 | (package.project_name, "(missing)" if package.is_missing else package.installed_version) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
651 | ) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
652 | package_key = mermaid_id(package.key) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
653 | nodes.add(f'{package_key}["{package_label}"]') |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
654 | for reverse_dependency in reverse_dependencies: |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
655 | edge_label = reverse_dependency.req.version_spec or "any" |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
656 | reverse_dependency_key = mermaid_id(reverse_dependency.key) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
657 | edges.add(f'{package_key} -- "{edge_label}" --> {reverse_dependency_key}') |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
658 | else: |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
659 | for package, dependencies in tree.items(): |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
660 | package_label = "\\n".join((package.project_name, package.version)) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
661 | package_key = mermaid_id(package.key) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
662 | nodes.add(f'{package_key}["{package_label}"]') |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
663 | for dependency in dependencies: |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
664 | edge_label = dependency.version_spec or "any" |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
665 | dependency_key = mermaid_id(dependency.key) |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
666 | if dependency.is_missing: |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
667 | dependency_label = f"{dependency.project_name}\\n(missing)" |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
668 | nodes.add(f'{dependency_key}["{dependency_label}"]:::missing') |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
669 | edges.add(f"{package_key} -.-> {dependency_key}") |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
670 | else: |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
671 | edges.add(f'{package_key} -- "{edge_label}" --> {dependency_key}') |
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
672 | |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
673 | # Produce the Mermaid Markdown. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
674 | indent = " " * 4 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
675 | output = dedent( |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
676 | f"""\ |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
677 | flowchart TD |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
678 | {indent}classDef missing stroke-dasharray: 5 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
679 | """ |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
680 | ) |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
681 | # Sort the nodes and edges to make the output deterministic. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
682 | output += indent |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
683 | output += f"\n{indent}".join(node for node in sorted(nodes)) |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
684 | output += "\n" + indent |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
685 | output += f"\n{indent}".join(edge for edge in sorted(edges)) |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
686 | output += "\n" |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
687 | return output |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
688 | |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
689 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
690 | def dump_graphviz(tree, output_format="dot", is_reverse=False): |
9218 | 691 | """Output dependency graph as one of the supported GraphViz output formats. |
692 | ||
693 | :param dict tree: dependency graph | |
694 | :param string output_format: output format | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
695 | :param bool is_reverse: reverse or not |
9218 | 696 | :returns: representation of tree in the specified output format |
697 | :rtype: str or binary representation depending on the output format | |
698 | ||
699 | """ | |
700 | try: | |
701 | from graphviz import Digraph | |
702 | except ImportError: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
703 | print("graphviz is not available, but necessary for the output " "option. Please install it.", file=sys.stderr) |
9218 | 704 | sys.exit(1) |
705 | ||
706 | try: | |
707 | from graphviz import parameters | |
708 | except ImportError: | |
709 | from graphviz import backend | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
710 | |
9218 | 711 | valid_formats = backend.FORMATS |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
712 | print( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
713 | "Deprecation warning! Please upgrade graphviz to version >=0.18.0 " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
714 | "Support for older versions will be removed in upcoming release", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
715 | file=sys.stderr, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
716 | ) |
9218 | 717 | else: |
718 | valid_formats = parameters.FORMATS | |
719 | ||
720 | if output_format not in valid_formats: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
721 | print(f"{output_format} is not a supported output format.", file=sys.stderr) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
722 | print(f"Supported formats are: {', '.join(sorted(valid_formats))}", file=sys.stderr) |
9218 | 723 | sys.exit(1) |
724 | ||
725 | graph = Digraph(format=output_format) | |
726 | ||
727 | if not is_reverse: | |
728 | for pkg, deps in tree.items(): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
729 | pkg_label = f"{pkg.project_name}\\n{pkg.version}" |
9218 | 730 | graph.node(pkg.key, label=pkg_label) |
731 | for dep in deps: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
732 | edge_label = dep.version_spec or "any" |
9218 | 733 | if dep.is_missing: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
734 | dep_label = f"{dep.project_name}\\n(missing)" |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
735 | graph.node(dep.key, label=dep_label, style="dashed") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
736 | graph.edge(pkg.key, dep.key, style="dashed") |
9218 | 737 | else: |
738 | graph.edge(pkg.key, dep.key, label=edge_label) | |
739 | else: | |
740 | for dep, parents in tree.items(): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
741 | dep_label = f"{dep.project_name}\\n{dep.installed_version}" |
9218 | 742 | graph.node(dep.key, label=dep_label) |
743 | for parent in parents: | |
744 | # req reference of the dep associated with this | |
745 | # particular parent package | |
746 | req_ref = parent.req | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
747 | edge_label = req_ref.version_spec or "any" |
9218 | 748 | graph.edge(dep.key, parent.key, label=edge_label) |
749 | ||
750 | # Allow output of dot format, even if GraphViz isn't installed. | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
751 | if output_format == "dot": |
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
752 | # Emulates graphviz.dot.Dot.__iter__() to force the sorting of graph.body. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
753 | # Fixes https://github.com/tox-dev/pipdeptree/issues/188 |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
754 | # That way we can guarantee the output of the dot format is deterministic |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
755 | # and stable. |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
756 | return "".join([tuple(graph)[0]] + sorted(graph.body) + [graph._tail]) |
9218 | 757 | |
758 | # As it's unknown if the selected output format is binary or not, try to | |
759 | # decode it as UTF8 and only print it out in binary if that's not possible. | |
760 | try: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
761 | return graph.pipe().decode("utf-8") |
9218 | 762 | except UnicodeDecodeError: |
763 | return graph.pipe() | |
764 | ||
765 | ||
766 | def print_graphviz(dump_output): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
767 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
768 | Dump the data generated by GraphViz to stdout. |
9218 | 769 | |
770 | :param dump_output: The output from dump_graphviz | |
771 | """ | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
772 | if hasattr(dump_output, "encode"): |
9218 | 773 | print(dump_output) |
774 | else: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
775 | with os.fdopen(sys.stdout.fileno(), "wb") as bytestream: |
9218 | 776 | bytestream.write(dump_output) |
777 | ||
778 | ||
779 | def conflicting_deps(tree): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
780 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
781 | Returns dependencies which are not present or conflict with the requirements of other packages. |
9218 | 782 | |
783 | e.g. will warn if pkg1 requires pkg2==2.0 and pkg2==1.0 is installed | |
784 | ||
785 | :param tree: the requirements tree (dict) | |
786 | :returns: dict of DistPackage -> list of unsatisfied/unknown ReqPackage | |
787 | :rtype: dict | |
788 | """ | |
789 | conflicting = defaultdict(list) | |
790 | for p, rs in tree.items(): | |
791 | for req in rs: | |
792 | if req.is_conflicting(): | |
793 | conflicting[p].append(req) | |
794 | return conflicting | |
795 | ||
796 | ||
797 | def render_conflicts_text(conflicts): | |
798 | if conflicts: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
799 | print("Warning!!! Possibly conflicting dependencies found:", file=sys.stderr) |
9218 | 800 | # Enforce alphabetical order when listing conflicts |
801 | pkgs = sorted(conflicts.keys()) | |
802 | for p in pkgs: | |
803 | pkg = p.render_as_root(False) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
804 | print(f"* {pkg}", file=sys.stderr) |
9218 | 805 | for req in conflicts[p]: |
806 | req_str = req.render_as_branch(False) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
807 | print(f" - {req_str}", file=sys.stderr) |
9218 | 808 | |
809 | ||
810 | def cyclic_deps(tree): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
811 | """ |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
812 | Return cyclic dependencies as list of tuples |
9218 | 813 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
814 | :param PackageDAG tree: package tree/dag |
9218 | 815 | :returns: list of tuples representing cyclic dependencies |
816 | :rtype: list | |
817 | """ | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
818 | index = {p.key: {r.key for r in rs} for p, rs in tree.items()} |
9218 | 819 | cyclic = [] |
820 | for p, rs in tree.items(): | |
821 | for r in rs: | |
822 | if p.key in index.get(r.key, []): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
823 | p_as_dep_of_r = [x for x in tree.get(tree.get_node_as_parent(r.key)) if x.key == p.key][0] |
9218 | 824 | cyclic.append((p, r, p_as_dep_of_r)) |
825 | return cyclic | |
826 | ||
827 | ||
828 | def render_cycles_text(cycles): | |
829 | if cycles: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
830 | print("Warning!! Cyclic dependencies found:", file=sys.stderr) |
9218 | 831 | # List in alphabetical order of the dependency that's cycling |
832 | # (2nd item in the tuple) | |
833 | cycles = sorted(cycles, key=lambda xs: xs[1].key) | |
834 | for a, b, c in cycles: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
835 | print(f"* {a.project_name} => {b.project_name} => {c.project_name}", file=sys.stderr) |
9218 | 836 | |
837 | ||
838 | def get_parser(): | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
839 | parser = argparse.ArgumentParser(description="Dependency tree of the installed python packages") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
840 | parser.add_argument("-v", "--version", action="version", version=f"{__version__}") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
841 | parser.add_argument("-f", "--freeze", action="store_true", help="Print names so as to write freeze files") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
842 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
843 | "--python", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
844 | default=sys.executable, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
845 | help="Python to use to look for packages in it (default: where" " installed)", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
846 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
847 | parser.add_argument("-a", "--all", action="store_true", help="list all deps at top level") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
848 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
849 | "-l", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
850 | "--local-only", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
851 | action="store_true", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
852 | help="If in a virtualenv that has global access " "do not show globally installed packages", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
853 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
854 | parser.add_argument("-u", "--user-only", action="store_true", help="Only show installations in the user site dir") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
855 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
856 | "-w", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
857 | "--warn", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
858 | action="store", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
859 | dest="warn", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
860 | nargs="?", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
861 | default="suppress", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
862 | choices=("silence", "suppress", "fail"), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
863 | help=( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
864 | 'Warning control. "suppress" will show warnings ' |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
865 | "but return 0 whether or not they are present. " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
866 | '"silence" will not show warnings at all and ' |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
867 | 'always return 0. "fail" will show warnings and ' |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
868 | "return 1 if any are present. The default is " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
869 | '"suppress".' |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
870 | ), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
871 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
872 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
873 | "-r", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
874 | "--reverse", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
875 | action="store_true", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
876 | default=False, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
877 | help=( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
878 | "Shows the dependency tree in the reverse fashion " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
879 | "ie. the sub-dependencies are listed with the " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
880 | "list of packages that need them under them." |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
881 | ), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
882 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
883 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
884 | "-p", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
885 | "--packages", |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
886 | help=( |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
887 | "Comma separated list of select packages to show in the output. " |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
888 | "Wildcards are supported, like 'somepackage.*'. " |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
889 | "If set, --all will be ignored." |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
890 | ), |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
891 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
892 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
893 | "-e", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
894 | "--exclude", |
10026
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
895 | help=( |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
896 | "Comma separated list of select packages to exclude from the output. " |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
897 | "Wildcards are supported, like 'somepackage.*'. " |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
898 | "If set, --all will be ignored." |
617290a049f0
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9849
diff
changeset
|
899 | ), |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
900 | metavar="PACKAGES", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
901 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
902 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
903 | "-j", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
904 | "--json", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
905 | action="store_true", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
906 | default=False, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
907 | help=( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
908 | "Display dependency tree as json. This will yield " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
909 | '"raw" output that may be used by external tools. ' |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
910 | "This option overrides all other options." |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
911 | ), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
912 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
913 | parser.add_argument( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
914 | "--json-tree", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
915 | action="store_true", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
916 | default=False, |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
917 | help=( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
918 | "Display dependency tree as json which is nested " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
919 | "the same way as the plain text output printed by default. " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
920 | "This option overrides all other options (except --json)." |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
921 | ), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
922 | ) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
923 | parser.add_argument( |
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
924 | "--mermaid", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
925 | action="store_true", |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
926 | default=False, |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
927 | help=("Display dependency tree as a Maermaid graph. " "This option overrides all other options."), |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
928 | ) |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
929 | parser.add_argument( |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
930 | "--graph-output", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
931 | dest="output_format", |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
932 | help=( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
933 | "Print a dependency graph in the specified output " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
934 | "format. Available are all formats supported by " |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
935 | "GraphViz, e.g.: dot, jpeg, pdf, png, svg" |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
936 | ), |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
937 | ) |
9218 | 938 | return parser |
939 | ||
940 | ||
941 | def _get_args(): | |
942 | parser = get_parser() | |
943 | return parser.parse_args() | |
944 | ||
945 | ||
946 | def handle_non_host_target(args): | |
947 | of_python = os.path.abspath(args.python) | |
948 | # if target is not current python re-invoke it under the actual host | |
949 | if of_python != os.path.abspath(sys.executable): | |
950 | # there's no way to guarantee that graphviz is available, so refuse | |
951 | if args.output_format: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
952 | print("graphviz functionality is not supported when querying" " non-host python", file=sys.stderr) |
9218 | 953 | raise SystemExit(1) |
954 | argv = sys.argv[1:] # remove current python executable | |
955 | for py_at, value in enumerate(argv): | |
956 | if value == "--python": | |
957 | del argv[py_at] | |
958 | del argv[py_at] | |
959 | elif value.startswith("--python"): | |
960 | del argv[py_at] | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
961 | |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
962 | main_file = inspect.getsourcefile(sys.modules[__name__]) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
963 | with tempfile.TemporaryDirectory() as project: |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
964 | dest = os.path.join(project, "pipdeptree") |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
965 | shutil.copytree(os.path.dirname(main_file), dest) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
966 | # invoke from an empty folder to avoid cwd altering sys.path |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
967 | env = os.environ.copy() |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
968 | env["PYTHONPATH"] = project |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
969 | cmd = [of_python, "-m", "pipdeptree"] |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
970 | cmd.extend(argv) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
971 | return subprocess.call(cmd, cwd=project, env=env) |
9218 | 972 | return None |
973 | ||
974 | ||
975 | def get_installed_distributions(local_only=False, user_only=False): | |
976 | try: | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
977 | from pip._internal.metadata import pkg_resources |
9218 | 978 | except ImportError: |
979 | # For backward compatibility with python ver. 2.7 and pip | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
980 | # version 20.3.4 (the latest pip version that works with python |
9218 | 981 | # version 2.7) |
982 | from pip._internal.utils import misc | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
983 | |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
984 | return misc.get_installed_distributions(local_only=local_only, user_only=user_only) |
9218 | 985 | else: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
986 | dists = pkg_resources.Environment.from_paths(None).iter_installed_distributions( |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
987 | local_only=local_only, skip=(), user_only=user_only |
9218 | 988 | ) |
989 | return [d._dist for d in dists] | |
990 | ||
991 | ||
992 | def main(): | |
993 | args = _get_args() | |
994 | result = handle_non_host_target(args) | |
995 | if result is not None: | |
996 | return result | |
997 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
998 | pkgs = get_installed_distributions(local_only=args.local_only, user_only=args.user_only) |
9218 | 999 | |
1000 | tree = PackageDAG.from_pkgs(pkgs) | |
1001 | ||
1002 | is_text_output = not any([args.json, args.json_tree, args.output_format]) | |
1003 | ||
1004 | return_code = 0 | |
1005 | ||
1006 | # Before any reversing or filtering, show warnings to console | |
1007 | # about possibly conflicting or cyclic deps if found and warnings | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1008 | # are enabled (i.e. only if output is to be printed to console) |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1009 | if is_text_output and args.warn != "silence": |
9218 | 1010 | conflicts = conflicting_deps(tree) |
1011 | if conflicts: | |
1012 | render_conflicts_text(conflicts) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1013 | print("-" * 72, file=sys.stderr) |
9218 | 1014 | |
1015 | cycles = cyclic_deps(tree) | |
1016 | if cycles: | |
1017 | render_cycles_text(cycles) | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1018 | print("-" * 72, file=sys.stderr) |
9218 | 1019 | |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1020 | if args.warn == "fail" and (conflicts or cycles): |
9218 | 1021 | return_code = 1 |
1022 | ||
1023 | # Reverse the tree (if applicable) before filtering, thus ensuring | |
1024 | # that the filter will be applied on ReverseTree | |
1025 | if args.reverse: | |
1026 | tree = tree.reverse() | |
1027 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1028 | show_only = set(args.packages.split(",")) if args.packages else None |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1029 | exclude = set(args.exclude.split(",")) if args.exclude else None |
9218 | 1030 | |
1031 | if show_only is not None or exclude is not None: | |
1032 | tree = tree.filter(show_only, exclude) | |
1033 | ||
1034 | if args.json: | |
1035 | print(render_json(tree, indent=4)) | |
1036 | elif args.json_tree: | |
1037 | print(render_json_tree(tree, indent=4)) | |
9849
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
1038 | elif args.mermaid: |
99782ca569ed
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
1039 | print(render_mermaid(tree)) |
9218 | 1040 | elif args.output_format: |
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1041 | output = dump_graphviz(tree, output_format=args.output_format, is_reverse=args.reverse) |
9218 | 1042 | print_graphviz(output) |
1043 | else: | |
1044 | render_text(tree, args.all, args.freeze) | |
1045 | ||
1046 | return return_code | |
1047 | ||
9589
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1048 | # |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1049 | # eric-ide modification: entry point to get one self-contained script |
09218eb3ae21
Third Party packages
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9218
diff
changeset
|
1050 | # |
9218 | 1051 | |
1052 | if __name__ == '__main__': | |
1053 | sys.exit(main()) |