Skip to content

Commit 46f1caf

Browse files
committed
Merge branch 'release-0.0.5'
2 parents b1f71d6 + b542816 commit 46f1caf

File tree

3 files changed

+108
-95
lines changed

3 files changed

+108
-95
lines changed

CHANGELOG.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
33

44
.. Keep the current version number on line number 5
5+
0.0.5
6+
=====
7+
8+
2019-11-08
9+
10+
* Rewrite implementation
11+
12+
513
0.0.4
614
=====
715

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ Source code
4444
* https://github.com/sinoroc/deptree
4545

4646

47+
Details
48+
=======
49+
50+
Similar projects
51+
----------------
52+
53+
* `pipdeptree`_
54+
55+
4756
Hacking
4857
=======
4958

@@ -91,6 +100,7 @@ Outside of a Python virtual environment run the following command::
91100
.. Links
92101
93102
.. _`GNU Make`: https://www.gnu.org/software/make/
103+
.. _`pipdeptree`: https://pypi.org/project/pipdeptree/
94104
.. _`pytest`: https://pytest.org/
95105
.. _`tox`: https://tox.readthedocs.io/
96106

src/deptree/_pkg_resources.py

Lines changed: 90 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,21 @@
1313

1414
def _display_conflict(distribution, requirement, depth):
1515
print(
16-
"{}{}=={} # CONFLICT {}".format(
16+
"{}{}=={} # !!! CONFLICT {}".format(
1717
' ' * INDENTATION * depth,
1818
distribution.project_name,
1919
distribution.version,
20-
requirement,
20+
str(requirement),
2121
),
2222
)
2323

2424

25-
def _display_cyclic(distribution, requirement, depth):
25+
def _display_cyclic(requirement, depth):
2626
print(
27-
"{}{}=={} # CYCLIC {}".format(
27+
"{}{} # !!! CYCLIC {}".format(
2828
' ' * INDENTATION * depth,
29-
distribution.project_name,
30-
distribution.version,
31-
requirement,
29+
requirement.project_name,
30+
str(requirement),
3231
),
3332
)
3433

@@ -39,116 +38,104 @@ def _display_good(distribution, requirement, depth):
3938
' ' * INDENTATION * depth,
4039
distribution.project_name,
4140
distribution.version,
42-
requirement,
41+
str(requirement) if requirement else '',
4342
),
4443
)
4544

4645

4746
def _display_missing(requirement, depth):
4847
print(
49-
"{}{} # MISSING {}".format(
48+
"{}{} # !!! MISSING {}".format(
5049
' ' * INDENTATION * depth,
5150
requirement.project_name,
52-
requirement,
51+
str(requirement),
5352
),
5453
)
5554

5655

57-
def _display_missing_reverse(requirement, depth):
58-
print(
59-
"{}{} # MISSING".format(
60-
' ' * INDENTATION * depth,
61-
requirement.project_name,
62-
),
63-
)
56+
def _display_forward(project_req):
57+
return _display_forward_one(project_req, [])
6458

6559

66-
def _display(distributions, requirement, chain):
60+
def _display_forward_one(project_req, chain):
6761
depth = len(chain)
68-
try:
69-
distribution = pkg_resources.get_distribution(requirement)
70-
except pkg_resources.VersionConflict:
71-
distribution = pkg_resources.get_distribution(requirement.key)
72-
_display_conflict(distribution, requirement, depth)
73-
except pkg_resources.DistributionNotFound:
74-
_display_missing(requirement, depth)
75-
except IndexError:
76-
# https://github.com/pypa/setuptools/issues/1677
77-
_display_missing(requirement, depth)
62+
project_key = project_req.key
63+
if project_key in chain:
64+
_display_cyclic(project_req, depth)
7865
else:
79-
if distribution.key in chain:
80-
_display_cyclic(distribution, requirement, depth)
66+
try:
67+
distribution = pkg_resources.get_distribution(project_req)
68+
except pkg_resources.DistributionNotFound:
69+
_display_missing(project_req, depth)
70+
except IndexError:
71+
# https://github.com/pypa/setuptools/issues/1677
72+
_display_missing(project_req, depth)
73+
except pkg_resources.VersionConflict:
74+
distribution = pkg_resources.get_distribution(project_key)
75+
_display_conflict(distribution, project_req, depth)
8176
else:
82-
_display_good(distribution, requirement, depth)
83-
try:
84-
dependencies = distribution.requires(extras=requirement.extras)
85-
except pkg_resources.UnknownExtra as exception:
86-
print(exception)
87-
else:
88-
sorted_dependency_keys = sorted([
89-
dependency.key
90-
for dependency
91-
in dependencies
92-
])
93-
all_deps = distributions[distribution.key]['dependencies']
94-
for dependency_key in sorted_dependency_keys:
95-
dependency_requirement = all_deps[dependency_key]
96-
_display(
97-
distributions,
98-
dependency_requirement,
99-
chain + [distribution.key],
100-
)
101-
102-
103-
def _display_reverse(distributions, project_req, dependency_req, chain):
77+
_display_good(distribution, project_req, depth)
78+
dependency_reqs = sorted(
79+
distribution.requires(extras=project_req.extras),
80+
key=lambda req: req.key,
81+
)
82+
for dependency_req in dependency_reqs:
83+
_display_forward_one(
84+
dependency_req,
85+
chain + [distribution.key],
86+
)
87+
88+
89+
def _display_reverse(distributions, project_requirement):
90+
return _display_reverse_one(distributions, project_requirement, None, [])
91+
92+
93+
def _display_reverse_one(distributions, project_req, dependency_req, chain):
10494
depth = len(chain)
105-
try:
106-
project_dist = pkg_resources.get_distribution(project_req)
107-
except pkg_resources.DistributionNotFound:
108-
_display_missing_reverse(project_req, depth)
95+
project_key = project_req.key
96+
if project_key in chain:
97+
_display_cyclic(project_req, depth)
10998
else:
110-
if project_dist.key in chain:
111-
_display_cyclic(project_dist, dependency_req, depth)
99+
try:
100+
distribution = pkg_resources.get_distribution(project_req)
101+
except pkg_resources.DistributionNotFound:
102+
_display_missing(project_req, depth)
112103
else:
113-
if dependency_req:
114-
try:
115-
pkg_resources.get_distribution(dependency_req)
116-
except pkg_resources.VersionConflict:
117-
_display_conflict(project_dist, dependency_req, depth)
118-
else:
119-
_display_good(project_dist, dependency_req, depth)
120-
else:
121-
_display_good(project_dist, '', depth)
122-
dependents = distributions[project_dist.key]['dependents']
123-
for (dependent_key, dependent_req) in sorted(dependents.items()):
124-
_display_reverse(
125-
distributions,
126-
dependent_key,
127-
dependent_req,
128-
chain + [project_dist.key],
129-
)
104+
_display_good(distribution, dependency_req, depth)
105+
dependents = distributions[project_key]['dependents']
106+
for (dependent_key, next_dependency_req) in sorted(dependents.items()):
107+
dependent_req = pkg_resources.Requirement.parse(dependent_key)
108+
_display_reverse_one(
109+
distributions,
110+
dependent_req,
111+
next_dependency_req,
112+
chain + [project_key],
113+
)
130114

131115

132116
def _discover_distributions():
133-
working_set = pkg_resources.working_set
134117
distributions = {}
118+
working_set = pkg_resources.working_set
135119
for distribution in working_set: # pylint: disable=not-an-iterable
136-
key = distribution.key
137-
if key not in distributions:
138-
distributions[key] = {
139-
'dependencies': {},
120+
dist_key = distribution.key
121+
dist_val = distributions.setdefault(
122+
dist_key,
123+
{
140124
'dependents': {},
141-
}
142-
distributions[key]['installed'] = True
143-
for requirement in distribution.requires(extras=distribution.extras):
144-
distributions[key]['dependencies'][requirement.key] = requirement
145-
if requirement.key not in distributions:
146-
distributions[requirement.key] = {
147-
'dependencies': {},
125+
'has_dependencies': False,
126+
},
127+
)
128+
for dependency_requirement in distribution.requires():
129+
dep_key = dependency_requirement.key
130+
dep_val = distributions.setdefault(
131+
dep_key,
132+
{
148133
'dependents': {},
149-
'installed': False,
150-
}
151-
distributions[requirement.key]['dependents'][key] = requirement
134+
'has_dependencies': False,
135+
},
136+
)
137+
dep_val['dependents'][dist_key] = dependency_requirement
138+
dist_val['has_dependencies'] = True
152139
return distributions
153140

154141

@@ -157,7 +144,11 @@ def _select_top_level(distributions):
157144
key
158145
for (key, info)
159146
in sorted(distributions.items())
160-
if not info['dependents']
147+
if (
148+
len(info['dependents']) == 0
149+
or
150+
(key in info['dependents'] and len(info['dependents']) == 1)
151+
)
161152
]
162153
return selection
163154

@@ -167,25 +158,29 @@ def _select_bottom_level(distributions):
167158
key
168159
for (key, info)
169160
in sorted(distributions.items())
170-
if info['installed'] and not info['dependencies']
161+
if not info['has_dependencies']
171162
]
172163
return selection
173164

174165

175166
def main(selection, reverse):
176167
""" Main function """
177-
distributions = _discover_distributions()
168+
distributions = None
169+
if reverse or not selection:
170+
distributions = _discover_distributions()
171+
178172
if not selection:
179173
if reverse:
180174
selection = _select_bottom_level(distributions)
181175
else:
182176
selection = _select_top_level(distributions)
177+
183178
for item in selection:
184179
requirement = pkg_resources.Requirement.parse(item)
185180
if reverse:
186-
_display_reverse(distributions, requirement, None, [])
181+
_display_reverse(distributions, requirement)
187182
else:
188-
_display(distributions, requirement, [])
183+
_display_forward(requirement)
189184

190185

191186
# EOF

0 commit comments

Comments
 (0)