pipgrip 0.11.0__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pipgrip-0.11.0/src/pipgrip.egg-info → pipgrip-0.12.0}/PKG-INFO +20 -17
- {pipgrip-0.11.0 → pipgrip-0.12.0}/README.md +19 -16
- pipgrip-0.12.0/src/pipgrip/_repo_version.py +1 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/cli.py +171 -29
- {pipgrip-0.11.0 → pipgrip-0.12.0/src/pipgrip.egg-info}/PKG-INFO +20 -17
- pipgrip-0.11.0/src/pipgrip/_repo_version.py +0 -1
- {pipgrip-0.11.0 → pipgrip-0.12.0}/LICENSE +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/requirements/prod.txt +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/setup.cfg +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/setup.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/__init__.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/compat.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/__init__.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/__init__.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/_compat.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/assignment.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/constraint.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/failure.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/incompatibility.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/incompatibility_cause.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/package.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/package_source.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/partial_solution.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/range.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/result.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/set_relation.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/term.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/union.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/mixology/version_solver.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/__init__.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/empty_constraint.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/exceptions.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/patterns.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/version.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/version_constraint.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/version_range.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/libs/semver/version_union.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/package_source.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip/pipper.py +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip.egg-info/SOURCES.txt +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip.egg-info/dependency_links.txt +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip.egg-info/entry_points.txt +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip.egg-info/requires.txt +0 -0
- {pipgrip-0.11.0 → pipgrip-0.12.0}/src/pipgrip.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipgrip
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm
|
|
5
5
|
Home-page: https://github.com/ddelange/pipgrip
|
|
6
6
|
Author: ddelange
|
|
@@ -64,7 +64,7 @@ Dynamic: summary
|
|
|
64
64
|
[](https://pypi.org/project/pipgrip/)
|
|
65
65
|
[](https://pypistats.org/packages/pipgrip)
|
|
66
66
|
|
|
67
|
-
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches
|
|
67
|
+
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches Python wheel metadata necessary for version solving (with fallback to building the wheel if no metadata is available), and optionally renders the full resulting dependency tree.
|
|
68
68
|
|
|
69
69
|
```
|
|
70
70
|
$ pipgrip --tree fastapi~=0.94
|
|
@@ -123,9 +123,10 @@ Usage: pipgrip [OPTIONS] [DEPENDENCIES]...
|
|
|
123
123
|
|
|
124
124
|
pipgrip is a lightweight pip dependency resolver with deptree preview
|
|
125
125
|
functionality based on the PubGrub algorithm, which is also used by poetry. For
|
|
126
|
-
one or more PEP 508 dependency specifications, pipgrip recursively
|
|
127
|
-
|
|
128
|
-
renders the full
|
|
126
|
+
one or more PEP 508 dependency specifications, pipgrip recursively fetches
|
|
127
|
+
Python wheel metadata necessary for version solving (with fallback to building
|
|
128
|
+
the wheel if no metadata is available), and optionally renders the full
|
|
129
|
+
resulting dependency tree.
|
|
129
130
|
|
|
130
131
|
Options:
|
|
131
132
|
--install Install full dependency tree after resolving.
|
|
@@ -139,8 +140,8 @@ Options:
|
|
|
139
140
|
--pipe Output space-separated pins instead of newline-
|
|
140
141
|
separated pins.
|
|
141
142
|
--json Output pins as JSON dict instead of newline-
|
|
142
|
-
separated pins. Combine with --tree
|
|
143
|
-
nested JSON dependency tree.
|
|
143
|
+
separated pins. Combine with --tree or --reversed-
|
|
144
|
+
tree for a detailed nested JSON dependency tree.
|
|
144
145
|
--sort Sort pins alphabetically before writing out. Can
|
|
145
146
|
be used bare, or in combination with --lock,
|
|
146
147
|
--pipe, --json, --tree-json, or --tree-json-exact.
|
|
@@ -153,6 +154,8 @@ Options:
|
|
|
153
154
|
--tree-ascii Output human readable dependency tree with ASCII
|
|
154
155
|
tree markers.
|
|
155
156
|
--reversed-tree Output human readable dependency tree (bottom-up).
|
|
157
|
+
--reversed-tree-ascii Output human readable dependency tree (bottom-up)
|
|
158
|
+
with ASCII tree markers.
|
|
156
159
|
--max-depth INTEGER Maximum (JSON) tree rendering depth (default -1).
|
|
157
160
|
--cache-dir DIRECTORY Use a custom cache dir.
|
|
158
161
|
--no-cache-dir Disable pip cache for the wheels downloaded by
|
|
@@ -170,8 +173,8 @@ Options:
|
|
|
170
173
|
dependencies (WARNING), -vv will show solving
|
|
171
174
|
decisions (INFO), -vvv for development (DEBUG).
|
|
172
175
|
--skip-invalid-input Skip invalid requirements (e.g. internal
|
|
173
|
-
repositories, typos) and continue processing
|
|
174
|
-
|
|
176
|
+
repositories, typos) and continue processing other
|
|
177
|
+
dependencies.
|
|
175
178
|
--version Show the version and exit.
|
|
176
179
|
-h, --help Show this message and exit.
|
|
177
180
|
```
|
|
@@ -183,14 +186,14 @@ Exhaustive dependency trees without the need to install any packages ([at most b
|
|
|
183
186
|
```
|
|
184
187
|
$ pipgrip --tree pipgrip
|
|
185
188
|
|
|
186
|
-
pipgrip (0.
|
|
187
|
-
├── anytree>=2.4.1 (2.
|
|
188
|
-
|
|
189
|
-
├──
|
|
190
|
-
├──
|
|
191
|
-
├──
|
|
192
|
-
|
|
193
|
-
└──
|
|
189
|
+
pipgrip (0.12.0)
|
|
190
|
+
├── anytree>=2.4.1 (2.13.0)
|
|
191
|
+
├── click>=7 (8.3.1)
|
|
192
|
+
├── packaging>=17 (26.0)
|
|
193
|
+
├── pip>=22.2 (26.0)
|
|
194
|
+
├── setuptools<81,>=38.3 (80.10.2)
|
|
195
|
+
└── wheel (0.46.3)
|
|
196
|
+
└── packaging>=24.0 (26.0)
|
|
194
197
|
```
|
|
195
198
|
|
|
196
199
|
For more details/further processing, combine `--tree` with `--json` for a detailed nested JSON dependency tree. See also `--tree-ascii` (no unicode tree markers), and `--tree-json` & `--tree-json-exact` (simplified JSON dependency trees).
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://pypi.org/project/pipgrip/)
|
|
8
8
|
[](https://pypistats.org/packages/pipgrip)
|
|
9
9
|
|
|
10
|
-
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches
|
|
10
|
+
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches Python wheel metadata necessary for version solving (with fallback to building the wheel if no metadata is available), and optionally renders the full resulting dependency tree.
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
$ pipgrip --tree fastapi~=0.94
|
|
@@ -66,9 +66,10 @@ Usage: pipgrip [OPTIONS] [DEPENDENCIES]...
|
|
|
66
66
|
|
|
67
67
|
pipgrip is a lightweight pip dependency resolver with deptree preview
|
|
68
68
|
functionality based on the PubGrub algorithm, which is also used by poetry. For
|
|
69
|
-
one or more PEP 508 dependency specifications, pipgrip recursively
|
|
70
|
-
|
|
71
|
-
renders the full
|
|
69
|
+
one or more PEP 508 dependency specifications, pipgrip recursively fetches
|
|
70
|
+
Python wheel metadata necessary for version solving (with fallback to building
|
|
71
|
+
the wheel if no metadata is available), and optionally renders the full
|
|
72
|
+
resulting dependency tree.
|
|
72
73
|
|
|
73
74
|
Options:
|
|
74
75
|
--install Install full dependency tree after resolving.
|
|
@@ -82,8 +83,8 @@ Options:
|
|
|
82
83
|
--pipe Output space-separated pins instead of newline-
|
|
83
84
|
separated pins.
|
|
84
85
|
--json Output pins as JSON dict instead of newline-
|
|
85
|
-
separated pins. Combine with --tree
|
|
86
|
-
nested JSON dependency tree.
|
|
86
|
+
separated pins. Combine with --tree or --reversed-
|
|
87
|
+
tree for a detailed nested JSON dependency tree.
|
|
87
88
|
--sort Sort pins alphabetically before writing out. Can
|
|
88
89
|
be used bare, or in combination with --lock,
|
|
89
90
|
--pipe, --json, --tree-json, or --tree-json-exact.
|
|
@@ -96,6 +97,8 @@ Options:
|
|
|
96
97
|
--tree-ascii Output human readable dependency tree with ASCII
|
|
97
98
|
tree markers.
|
|
98
99
|
--reversed-tree Output human readable dependency tree (bottom-up).
|
|
100
|
+
--reversed-tree-ascii Output human readable dependency tree (bottom-up)
|
|
101
|
+
with ASCII tree markers.
|
|
99
102
|
--max-depth INTEGER Maximum (JSON) tree rendering depth (default -1).
|
|
100
103
|
--cache-dir DIRECTORY Use a custom cache dir.
|
|
101
104
|
--no-cache-dir Disable pip cache for the wheels downloaded by
|
|
@@ -113,8 +116,8 @@ Options:
|
|
|
113
116
|
dependencies (WARNING), -vv will show solving
|
|
114
117
|
decisions (INFO), -vvv for development (DEBUG).
|
|
115
118
|
--skip-invalid-input Skip invalid requirements (e.g. internal
|
|
116
|
-
repositories, typos) and continue processing
|
|
117
|
-
|
|
119
|
+
repositories, typos) and continue processing other
|
|
120
|
+
dependencies.
|
|
118
121
|
--version Show the version and exit.
|
|
119
122
|
-h, --help Show this message and exit.
|
|
120
123
|
```
|
|
@@ -126,14 +129,14 @@ Exhaustive dependency trees without the need to install any packages ([at most b
|
|
|
126
129
|
```
|
|
127
130
|
$ pipgrip --tree pipgrip
|
|
128
131
|
|
|
129
|
-
pipgrip (0.
|
|
130
|
-
├── anytree>=2.4.1 (2.
|
|
131
|
-
|
|
132
|
-
├──
|
|
133
|
-
├──
|
|
134
|
-
├──
|
|
135
|
-
|
|
136
|
-
└──
|
|
132
|
+
pipgrip (0.12.0)
|
|
133
|
+
├── anytree>=2.4.1 (2.13.0)
|
|
134
|
+
├── click>=7 (8.3.1)
|
|
135
|
+
├── packaging>=17 (26.0)
|
|
136
|
+
├── pip>=22.2 (26.0)
|
|
137
|
+
├── setuptools<81,>=38.3 (80.10.2)
|
|
138
|
+
└── wheel (0.46.3)
|
|
139
|
+
└── packaging>=24.0 (26.0)
|
|
137
140
|
```
|
|
138
141
|
|
|
139
142
|
For more details/further processing, combine `--tree` with `--json` for a detailed nested JSON dependency tree. See also `--tree-ascii` (no unicode tree markers), and `--tree-json` & `--tree-json-exact` (simplified JSON dependency trees).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "0.12.0"
|
|
@@ -42,7 +42,7 @@ from multiprocessing import cpu_count
|
|
|
42
42
|
from subprocess import CalledProcessError
|
|
43
43
|
|
|
44
44
|
import click
|
|
45
|
-
from anytree import AsciiStyle, ContStyle, Node, RenderTree
|
|
45
|
+
from anytree import AsciiStyle, ContStyle, Node, PreOrderIter, RenderTree
|
|
46
46
|
from anytree.exporter import DictExporter
|
|
47
47
|
from packaging.markers import default_environment
|
|
48
48
|
from packaging.requirements import InvalidRequirement
|
|
@@ -107,6 +107,47 @@ class DepTreeDictExporter(DictExporter):
|
|
|
107
107
|
return data
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
class ReversedDepTreeDictExporter(DictExporter):
|
|
111
|
+
"""Export reversed tree in full detail, children renamed to dependents."""
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self, dictcls=OrderedDict, attriter=None, childiter=list, maxlevel=None
|
|
115
|
+
):
|
|
116
|
+
DictExporter.__init__(
|
|
117
|
+
self,
|
|
118
|
+
dictcls=dictcls,
|
|
119
|
+
attriter=attriter,
|
|
120
|
+
childiter=childiter,
|
|
121
|
+
maxlevel=maxlevel,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def customsort(cls, tup):
|
|
126
|
+
order = ["name", "extras_name", "version", "pip_string", "requires"]
|
|
127
|
+
k, v = tup
|
|
128
|
+
if k in order:
|
|
129
|
+
return (str(order.index(k)), 0)
|
|
130
|
+
return tup
|
|
131
|
+
|
|
132
|
+
def export(self, node):
|
|
133
|
+
"""Export tree starting at `node`."""
|
|
134
|
+
attriter = self.attriter or partial(sorted, key=self.customsort)
|
|
135
|
+
return self.__export(node, self.dictcls, attriter, self.childiter)
|
|
136
|
+
|
|
137
|
+
def __export(self, node, dictcls, attriter, childiter, level=1):
|
|
138
|
+
attr_values = attriter(self._iter_attr_values(node))
|
|
139
|
+
data = dictcls(attr_values)
|
|
140
|
+
maxlevel = self.maxlevel
|
|
141
|
+
if maxlevel is None or level < maxlevel:
|
|
142
|
+
children = [
|
|
143
|
+
self.__export(child, dictcls, attriter, childiter, level=level + 1)
|
|
144
|
+
for child in childiter(node.children)
|
|
145
|
+
]
|
|
146
|
+
if children:
|
|
147
|
+
data["dependents"] = children
|
|
148
|
+
return data
|
|
149
|
+
|
|
150
|
+
|
|
110
151
|
def flatten(tree_dict):
|
|
111
152
|
"""Flatten tree_dict to a shallow OrderedDict with all unique exact pins."""
|
|
112
153
|
out = OrderedDict()
|
|
@@ -221,16 +262,18 @@ def render_tree(tree_root, max_depth, tree_ascii=False):
|
|
|
221
262
|
for fill, _, node in RenderTree(child, style=style):
|
|
222
263
|
if max_depth and node.depth > max_depth:
|
|
223
264
|
continue
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
#
|
|
233
|
-
|
|
265
|
+
# Build the parenthetical part
|
|
266
|
+
requires = getattr(node, "requires", None)
|
|
267
|
+
# fmt: off
|
|
268
|
+
cyclic = u", cyclic" if hasattr(node, "cyclic") else u""
|
|
269
|
+
if requires:
|
|
270
|
+
# Reversed tree dependent: name (version requires spec)
|
|
271
|
+
paren = u"{} requires {}{}".format(node.version, requires, cyclic)
|
|
272
|
+
else:
|
|
273
|
+
# Normal node: name (version)
|
|
274
|
+
paren = u"{}{}".format(node.version, cyclic)
|
|
275
|
+
lines.append(u"{}{} ({})".format(fill, node.pip_string, paren))
|
|
276
|
+
# fmt: on
|
|
234
277
|
output += lines
|
|
235
278
|
return "\n".join(output)
|
|
236
279
|
|
|
@@ -254,6 +297,94 @@ def render_json_tree_full(tree_root, max_depth, sort):
|
|
|
254
297
|
return tree_dict_full
|
|
255
298
|
|
|
256
299
|
|
|
300
|
+
def reverse_tree(tree_root):
|
|
301
|
+
"""Reverse the dependency tree to show dependents instead of dependencies.
|
|
302
|
+
|
|
303
|
+
Creates a new tree where:
|
|
304
|
+
- Each unique package becomes a root-level node (sorted alphabetically)
|
|
305
|
+
- Children are dependents with their requirement spec inline
|
|
306
|
+
- Format: name (version requires spec)
|
|
307
|
+
"""
|
|
308
|
+
# Build reverse mapping: {extras_name: [(dependent_extras_name, dependent_node, req_spec), ...]}
|
|
309
|
+
# Use extras_name as key to distinguish packages with different extras (e.g., etils[enp] vs etils[epy])
|
|
310
|
+
reverse_map = {}
|
|
311
|
+
for node in PreOrderIter(tree_root):
|
|
312
|
+
if node.name == "__root__":
|
|
313
|
+
continue
|
|
314
|
+
for child in node.children:
|
|
315
|
+
# child is the dependency, node is the dependent
|
|
316
|
+
# child.pip_string = how node requires child (e.g., "numpy>=1.7")
|
|
317
|
+
child_key = child.extras_name
|
|
318
|
+
if child_key not in reverse_map:
|
|
319
|
+
reverse_map[child_key] = []
|
|
320
|
+
req_spec = child.pip_string
|
|
321
|
+
# Avoid duplicate entries for same dependent
|
|
322
|
+
if not any(d[0] == node.extras_name for d in reverse_map[child_key]):
|
|
323
|
+
reverse_map[child_key].append((node.extras_name, node, req_spec))
|
|
324
|
+
|
|
325
|
+
# Collect all unique packages with their resolved versions
|
|
326
|
+
all_packages = {} # extras_name -> (version, extras_name, extras)
|
|
327
|
+
for node in PreOrderIter(tree_root):
|
|
328
|
+
if node.name != "__root__":
|
|
329
|
+
all_packages[node.extras_name] = (
|
|
330
|
+
node.version,
|
|
331
|
+
node.extras_name,
|
|
332
|
+
node.extras,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def add_dependents(parent_node, pkg_extras_name, visited):
|
|
336
|
+
"""Recursively add dependents with their requirement spec."""
|
|
337
|
+
dependents = reverse_map.get(pkg_extras_name, [])
|
|
338
|
+
for dependent_extras_name, dependent_node, req_spec in sorted(
|
|
339
|
+
dependents, key=lambda x: x[0]
|
|
340
|
+
):
|
|
341
|
+
dep_child = Node(
|
|
342
|
+
dependent_node.name,
|
|
343
|
+
parent=parent_node,
|
|
344
|
+
version=dependent_node.version,
|
|
345
|
+
extras_name=dependent_node.extras_name,
|
|
346
|
+
extras=dependent_node.extras,
|
|
347
|
+
pip_string=dependent_node.extras_name, # Just the name
|
|
348
|
+
requires=req_spec, # How this dependent requires the parent
|
|
349
|
+
)
|
|
350
|
+
if dependent_extras_name in visited:
|
|
351
|
+
dep_child.cyclic = True
|
|
352
|
+
else:
|
|
353
|
+
# Recursively add this dependent's dependents
|
|
354
|
+
add_dependents(
|
|
355
|
+
dep_child, dependent_extras_name, visited | {dependent_extras_name}
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Create reversed tree
|
|
359
|
+
reversed_root = Node("__root__")
|
|
360
|
+
|
|
361
|
+
# Create root-level nodes for each package (sorted alphabetically by extras_name)
|
|
362
|
+
for pkg_extras_name in sorted(all_packages.keys()):
|
|
363
|
+
version, extras_name, extras = all_packages[pkg_extras_name]
|
|
364
|
+
pkg_node = Node(
|
|
365
|
+
pkg_extras_name.split("[")[0], # Base name for node.name
|
|
366
|
+
parent=reversed_root,
|
|
367
|
+
version=version,
|
|
368
|
+
extras_name=extras_name,
|
|
369
|
+
extras=extras,
|
|
370
|
+
pip_string=extras_name, # e.g., "numpy" or "etils[enp]" - version shown in parentheses
|
|
371
|
+
)
|
|
372
|
+
# Recursively add dependents
|
|
373
|
+
add_dependents(pkg_node, pkg_extras_name, {pkg_extras_name})
|
|
374
|
+
|
|
375
|
+
return reversed_root
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def render_reversed_json_tree_full(reversed_tree_root, max_depth, sort):
|
|
379
|
+
"""Render reversed tree to JSON with 'dependents' instead of 'dependencies'."""
|
|
380
|
+
maxlevel = max_depth + 1 if max_depth else None
|
|
381
|
+
exporter = ReversedDepTreeDictExporter(
|
|
382
|
+
maxlevel=maxlevel, attriter=sorted if sort else None
|
|
383
|
+
)
|
|
384
|
+
tree_dict_full = exporter.export(reversed_tree_root)["dependents"]
|
|
385
|
+
return tree_dict_full
|
|
386
|
+
|
|
387
|
+
|
|
257
388
|
def render_lock(packages, include_dot=True, sort=False):
|
|
258
389
|
fn = sorted if sort else list
|
|
259
390
|
return fn(
|
|
@@ -265,7 +396,7 @@ def render_lock(packages, include_dot=True, sort=False):
|
|
|
265
396
|
|
|
266
397
|
@click.command(
|
|
267
398
|
context_settings={"help_option_names": ["-h", "--help"], "max_content_width": 84},
|
|
268
|
-
help="pipgrip is a lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm, which is also used by poetry. For one or more PEP 508 dependency specifications, pipgrip recursively fetches
|
|
399
|
+
help="pipgrip is a lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm, which is also used by poetry. For one or more PEP 508 dependency specifications, pipgrip recursively fetches Python wheel metadata necessary for version solving (with fallback to building the wheel if no metadata is available), and optionally renders the full resulting dependency tree.",
|
|
269
400
|
)
|
|
270
401
|
@click.argument("dependencies", nargs=-1)
|
|
271
402
|
@click.option(
|
|
@@ -304,7 +435,7 @@ def render_lock(packages, include_dot=True, sort=False):
|
|
|
304
435
|
@click.option(
|
|
305
436
|
"--json",
|
|
306
437
|
is_flag=True,
|
|
307
|
-
help="Output pins as JSON dict instead of newline-separated pins. Combine with --tree for a detailed nested JSON dependency tree.",
|
|
438
|
+
help="Output pins as JSON dict instead of newline-separated pins. Combine with --tree or --reversed-tree for a detailed nested JSON dependency tree.",
|
|
308
439
|
)
|
|
309
440
|
@click.option(
|
|
310
441
|
"--sort",
|
|
@@ -338,6 +469,11 @@ def render_lock(packages, include_dot=True, sort=False):
|
|
|
338
469
|
is_flag=True,
|
|
339
470
|
help="Output human readable dependency tree (bottom-up).",
|
|
340
471
|
)
|
|
472
|
+
@click.option(
|
|
473
|
+
"--reversed-tree-ascii",
|
|
474
|
+
is_flag=True,
|
|
475
|
+
help="Output human readable dependency tree (bottom-up) with ASCII tree markers.",
|
|
476
|
+
)
|
|
341
477
|
@click.option(
|
|
342
478
|
"--max-depth",
|
|
343
479
|
type=click.INT,
|
|
@@ -407,6 +543,7 @@ def main(
|
|
|
407
543
|
tree_json,
|
|
408
544
|
tree_json_exact,
|
|
409
545
|
reversed_tree,
|
|
546
|
+
reversed_tree_ascii,
|
|
410
547
|
max_depth,
|
|
411
548
|
cache_dir,
|
|
412
549
|
no_cache_dir,
|
|
@@ -429,23 +566,22 @@ def main(
|
|
|
429
566
|
logger.debug("pip version: %s", PIP_VERSION)
|
|
430
567
|
logger.debug("pipgrip version: %s", __version__)
|
|
431
568
|
|
|
432
|
-
if (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
reversed_tree,
|
|
441
|
-
)
|
|
569
|
+
if sum(
|
|
570
|
+
(
|
|
571
|
+
pipe,
|
|
572
|
+
(json or tree or reversed_tree),
|
|
573
|
+
tree_ascii,
|
|
574
|
+
tree_json,
|
|
575
|
+
tree_json_exact,
|
|
576
|
+
reversed_tree_ascii,
|
|
442
577
|
)
|
|
443
|
-
|
|
444
|
-
):
|
|
578
|
+
) > 1 or (tree and (reversed_tree or reversed_tree_ascii)):
|
|
445
579
|
raise click.ClickException("Illegal combination of output formats selected")
|
|
446
580
|
|
|
447
|
-
if tree_ascii
|
|
581
|
+
if tree_ascii:
|
|
448
582
|
tree = True
|
|
583
|
+
if reversed_tree_ascii:
|
|
584
|
+
reversed_tree = True
|
|
449
585
|
elif tree_json_exact:
|
|
450
586
|
tree_json = True
|
|
451
587
|
|
|
@@ -561,9 +697,15 @@ def main(
|
|
|
561
697
|
)
|
|
562
698
|
|
|
563
699
|
if reversed_tree:
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
700
|
+
reversed_tree_root = reverse_tree(tree_root)
|
|
701
|
+
if json:
|
|
702
|
+
output = dumps(
|
|
703
|
+
render_reversed_json_tree_full(reversed_tree_root, max_depth, sort),
|
|
704
|
+
default=sorted,
|
|
705
|
+
)
|
|
706
|
+
else:
|
|
707
|
+
output = render_tree(reversed_tree_root, max_depth, reversed_tree_ascii)
|
|
708
|
+
elif tree:
|
|
567
709
|
if json:
|
|
568
710
|
output = dumps(
|
|
569
711
|
render_json_tree_full(tree_root, max_depth, sort), default=sorted
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipgrip
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Lightweight pip dependency resolver with deptree preview functionality based on the PubGrub algorithm
|
|
5
5
|
Home-page: https://github.com/ddelange/pipgrip
|
|
6
6
|
Author: ddelange
|
|
@@ -64,7 +64,7 @@ Dynamic: summary
|
|
|
64
64
|
[](https://pypi.org/project/pipgrip/)
|
|
65
65
|
[](https://pypistats.org/packages/pipgrip)
|
|
66
66
|
|
|
67
|
-
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches
|
|
67
|
+
[pipgrip](https://github.com/ddelange/pipgrip) is a lightweight pip dependency resolver with deptree preview functionality based on the [PubGrub algorithm](https://medium.com/@nex3/pubgrub-2fb6470504f), which is also used by [poetry](https://github.com/python-poetry/poetry). For one or more [PEP 508](https://www.python.org/dev/peps/pep-0508/) dependency specifications, pipgrip recursively fetches Python wheel metadata necessary for version solving (with fallback to building the wheel if no metadata is available), and optionally renders the full resulting dependency tree.
|
|
68
68
|
|
|
69
69
|
```
|
|
70
70
|
$ pipgrip --tree fastapi~=0.94
|
|
@@ -123,9 +123,10 @@ Usage: pipgrip [OPTIONS] [DEPENDENCIES]...
|
|
|
123
123
|
|
|
124
124
|
pipgrip is a lightweight pip dependency resolver with deptree preview
|
|
125
125
|
functionality based on the PubGrub algorithm, which is also used by poetry. For
|
|
126
|
-
one or more PEP 508 dependency specifications, pipgrip recursively
|
|
127
|
-
|
|
128
|
-
renders the full
|
|
126
|
+
one or more PEP 508 dependency specifications, pipgrip recursively fetches
|
|
127
|
+
Python wheel metadata necessary for version solving (with fallback to building
|
|
128
|
+
the wheel if no metadata is available), and optionally renders the full
|
|
129
|
+
resulting dependency tree.
|
|
129
130
|
|
|
130
131
|
Options:
|
|
131
132
|
--install Install full dependency tree after resolving.
|
|
@@ -139,8 +140,8 @@ Options:
|
|
|
139
140
|
--pipe Output space-separated pins instead of newline-
|
|
140
141
|
separated pins.
|
|
141
142
|
--json Output pins as JSON dict instead of newline-
|
|
142
|
-
separated pins. Combine with --tree
|
|
143
|
-
nested JSON dependency tree.
|
|
143
|
+
separated pins. Combine with --tree or --reversed-
|
|
144
|
+
tree for a detailed nested JSON dependency tree.
|
|
144
145
|
--sort Sort pins alphabetically before writing out. Can
|
|
145
146
|
be used bare, or in combination with --lock,
|
|
146
147
|
--pipe, --json, --tree-json, or --tree-json-exact.
|
|
@@ -153,6 +154,8 @@ Options:
|
|
|
153
154
|
--tree-ascii Output human readable dependency tree with ASCII
|
|
154
155
|
tree markers.
|
|
155
156
|
--reversed-tree Output human readable dependency tree (bottom-up).
|
|
157
|
+
--reversed-tree-ascii Output human readable dependency tree (bottom-up)
|
|
158
|
+
with ASCII tree markers.
|
|
156
159
|
--max-depth INTEGER Maximum (JSON) tree rendering depth (default -1).
|
|
157
160
|
--cache-dir DIRECTORY Use a custom cache dir.
|
|
158
161
|
--no-cache-dir Disable pip cache for the wheels downloaded by
|
|
@@ -170,8 +173,8 @@ Options:
|
|
|
170
173
|
dependencies (WARNING), -vv will show solving
|
|
171
174
|
decisions (INFO), -vvv for development (DEBUG).
|
|
172
175
|
--skip-invalid-input Skip invalid requirements (e.g. internal
|
|
173
|
-
repositories, typos) and continue processing
|
|
174
|
-
|
|
176
|
+
repositories, typos) and continue processing other
|
|
177
|
+
dependencies.
|
|
175
178
|
--version Show the version and exit.
|
|
176
179
|
-h, --help Show this message and exit.
|
|
177
180
|
```
|
|
@@ -183,14 +186,14 @@ Exhaustive dependency trees without the need to install any packages ([at most b
|
|
|
183
186
|
```
|
|
184
187
|
$ pipgrip --tree pipgrip
|
|
185
188
|
|
|
186
|
-
pipgrip (0.
|
|
187
|
-
├── anytree>=2.4.1 (2.
|
|
188
|
-
|
|
189
|
-
├──
|
|
190
|
-
├──
|
|
191
|
-
├──
|
|
192
|
-
|
|
193
|
-
└──
|
|
189
|
+
pipgrip (0.12.0)
|
|
190
|
+
├── anytree>=2.4.1 (2.13.0)
|
|
191
|
+
├── click>=7 (8.3.1)
|
|
192
|
+
├── packaging>=17 (26.0)
|
|
193
|
+
├── pip>=22.2 (26.0)
|
|
194
|
+
├── setuptools<81,>=38.3 (80.10.2)
|
|
195
|
+
└── wheel (0.46.3)
|
|
196
|
+
└── packaging>=24.0 (26.0)
|
|
194
197
|
```
|
|
195
198
|
|
|
196
199
|
For more details/further processing, combine `--tree` with `--json` for a detailed nested JSON dependency tree. See also `--tree-ascii` (no unicode tree markers), and `--tree-json` & `--tree-json-exact` (simplified JSON dependency trees).
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = "0.11.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|