Merge pull request #14 from msabramo/master_fix_infinite_recursion

Fix infinite recursion for cyclic deps (#13)
This commit is contained in:
Vineet Naik 2014-06-18 10:06:07 +05:30
commit adc549464e
6 changed files with 172 additions and 4 deletions

21
pickle_env.py Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
# This is a small tool to create a pickle file for a set of packages for the
# purposes of writing tests
import pickle
import sys
import pip
def main():
default_skip = ['setuptools', 'pip', 'python', 'distribute']
skip = default_skip + ['pipdeptree']
pkgs = pip.get_installed_distributions(local_only=True, skip=skip)
pickle.dump(pkgs, sys.stdout)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -130,7 +130,10 @@ def render_tree(pkgs, pkg_index, req_map, list_all,
non_top = set(r.key for r in flatten(req_map.values()))
top = [p for p in pkgs if p.key not in non_top]
def aux(pkg, indent=0):
def aux(pkg, indent=0, chain=None):
if chain is None:
chain = [pkg.project_name]
# In this function, pkg can either be a Distribution or
# Requirement instance
if indent > 0:
@ -150,14 +153,31 @@ def render_tree(pkgs, pkg_index, req_map, list_all,
# packages, eg. `testresources`, this will fail
if pkg.key in pkg_index:
pkg_deps = pkg_index[pkg.key].requires()
result += list(flatten([aux(d, indent=indent+2)
for d in pkg_deps]))
filtered_deps = [
aux(d, indent=indent+2, chain=chain+[d.project_name])
for d in pkg_deps
if d.project_name not in chain]
result += list(flatten(filtered_deps))
return result
lines = flatten([aux(p) for p in (pkgs if list_all else top)])
return '\n'.join(lines)
def cyclic_deps(pkgs, pkg_index):
def aux(pkg, chain):
if pkg.key in pkg_index:
for d in pkg_index[pkg.key].requires():
if d.project_name in chain:
yield ' => '.join([str(p) for p in chain] + [str(d)])
else:
for cycle in aux(d, chain=chain+[d.project_name]):
yield cycle
for cycle in flatten([aux(p, chain=[]) for p in pkgs]):
yield cycle
def main():
parser = argparse.ArgumentParser(description=(
'Dependency tree of the installed python packages'
@ -200,6 +220,12 @@ def main():
print(tmpl.format(pkg, req), file=sys.stderr)
print('-'*72, file=sys.stderr)
cyclic = cyclic_deps(pkgs, pkg_index)
if cyclic:
print('Warning!! Cyclic dependencies found:', file=sys.stderr)
for xs in cyclic:
print('- {0}'.format(xs))
if args.freeze:
top_pkg_str, non_top_pkg_str = top_pkg_src, non_top_pkg_src
else:

85
tests/cyclic_deps.pickle Normal file
View File

@ -0,0 +1,85 @@
(lp0
ccopy_reg
_reconstructor
p1
(cpip._vendor.pkg_resources
Distribution
p2
c__builtin__
object
p3
Ntp4
Rp5
(dp6
S'project_name'
p7
S'CircularDependencyA'
p8
sS'precedence'
p9
I-1
sS'_key'
p10
S'circulardependencya'
p11
sS'_version'
p12
S'0.0.0'
p13
sS'platform'
p14
NsS'location'
p15
S'.tox/cyclic_deps/lib/python2.7/site-packages'
p16
sS'py_version'
p17
S'2.7'
p18
sS'_provider'
p19
(ipip._vendor.pkg_resources
PathMetadata
p20
(dp21
S'module_path'
p22
g16
sS'egg_info'
p23
S'.tox/cyclic_deps/lib/python2.7/site-packages/CircularDependencyA-0.0.0-py2.7.egg-info'
p24
sbsbag1
(g2
g3
Ntp25
Rp26
(dp27
g7
S'CircularDependencyB'
p28
sg9
I-1
sg10
S'circulardependencyb'
p29
sg12
S'0.0.0'
p30
sg14
Nsg15
g16
sg17
S'2.7'
p31
sg19
(ipip._vendor.pkg_resources
PathMetadata
p32
(dp33
g22
g16
sg23
S'.tox/cyclic_deps/lib/python2.7/site-packages/CircularDependencyB-0.0.0-py2.7.egg-info'
p34
sbsba.

View File

@ -79,3 +79,33 @@ def test_render_tree_freeze():
assert ' - SQLAlchemy==0.9.1' in lines
assert '-e git+https://github.com/naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy-master' in lines
assert 'itsdangerous==0.23' not in lines
def test_render_tree_cyclic_dependency():
with open('tests/cyclic_deps.pickle', 'rb') as f:
cyclic_pkgs = pickle.load(f)
list_all = True
tree_str = render_tree(cyclic_pkgs, pkg_index, req_map, list_all,
top_pkg_name, non_top_pkg_name)
lines = set(tree_str.split('\n'))
assert 'CircularDependencyA==0.0.0' in lines
assert ' - CircularDependencyB [installed: 0.0.0]' in lines
assert 'CircularDependencyB==0.0.0' in lines
assert ' - CircularDependencyA [installed: 0.0.0]' in lines
def test_render_tree_freeze_cyclic_dependency():
with open('tests/cyclic_deps.pickle', 'rb') as f:
cyclic_pkgs = pickle.load(f)
list_all = True
tree_str = render_tree(cyclic_pkgs, pkg_index, req_map, list_all,
top_pkg_src, non_top_pkg_src)
lines = set(tree_str.split('\n'))
assert 'CircularDependencyA==0.0.0' in lines
assert ' - CircularDependencyB==0.0.0' in lines
assert 'CircularDependencyB==0.0.0' in lines
assert ' - CircularDependencyA==0.0.0' in lines

View File

@ -0,0 +1,2 @@
CircularDependencyA
CircularDependencyB

View File

@ -8,7 +8,7 @@ envlist = py26, py27, py32, py33, py34
[testenv]
commands =
tox -e dummy
tox -e dummy,cyclic_deps
py.test {posargs:--cov pipdeptree --cov-report xml --cov-report html --cov-report term-missing tests/}
deps =
pytest
@ -24,3 +24,7 @@ whitelist_externals = tox
[testenv:dummy]
deps = -r{toxinidir}/tests/virtualenvs/dummy_requirements.txt
commands =
[testenv:cyclic_deps]
deps = -r{toxinidir}/tests/virtualenvs/cyclic_deps_requirements.txt
commands =