Try guessing versions of pkgs not listed by pip

* 'pip.get_installed_distributions' doesn't include some packages such
  as 'pip', 'setuptools' etc. so the installed versions for these is not
  available. This change tries to guess the installed version by
  importing the module and checking if the version is defined in
  '__version__' variable.

* Another related change is that the 'required' and 'installed' versions
  will be shown for all intermediate packages. When 'required' is not
  specified, it will show 'None' and when 'installed' is not available,
  it will show '?'. This is to keep the output consistent with the confusing deps
  output.

* Fix indentation in output.

Kind of fixes #46.
This commit is contained in:
Vineet Naik 2016-08-06 18:39:32 +05:30
parent 9949bf66db
commit a44baabaa5
2 changed files with 33 additions and 26 deletions

View File

@ -5,6 +5,7 @@ from collections import defaultdict, OrderedDict
import argparse
from operator import attrgetter
import json
from importlib import import_module
import pip
import pkg_resources
@ -98,6 +99,23 @@ def reverse_tree(tree):
return rtree
def guess_version(pkg_key, default='?'):
"""Guess the version of a pkg when pip doesn't provide it
:param str pkg_key: key of the package
:param str default: default version to return if unable to find
:returns: version
:rtype: string
"""
try:
m = import_module(pkg_key)
except ImportError:
return default
else:
return getattr(m, '__version__', default)
class Package(object):
"""Abstract class for wrappers around objects that pip returns.
@ -209,9 +227,9 @@ class ReqPackage(Package):
@property
def installed_version(self):
# if the dist is None as in some cases, we don't know the
# installed version
return self.dist.version if self.dist else '?'
if not self.dist:
return guess_version(self.key)
return self.dist.version
def render_as_root(self, frozen):
if not frozen:
@ -223,15 +241,10 @@ class ReqPackage(Package):
def render_as_branch(self, frozen):
if not frozen:
vers = []
if self.version_spec:
vers.append(('required', self.version_spec))
if self.dist:
vers.append(('installed', self.installed_version))
if not vers:
return self.key
ver_str = ', '.join(['{0}: {1}'.format(k, v) for k, v in vers])
return '{0} [{1}]'.format(self.project_name, ver_str)
return (
'{0} [required: {1}, installed: {2}]'
).format(self.project_name, self.version_spec,
self.installed_version)
else:
return self.render_as_root(frozen)
@ -276,7 +289,7 @@ def render_tree(tree, list_all=True, show_only=None, frozen=False):
chain = [node.project_name]
node_str = node.render(parent, frozen)
if parent:
prefix = ' '*indent + ('-' if use_bullets else ' ') + ' '
prefix = ' '*indent + ('- ' if use_bullets else '')
node_str = prefix + node_str
result = [node_str]
children = [aux(c, node, indent=indent+2,
@ -422,13 +435,7 @@ def main():
pkg = p.render_as_root(False)
print('* %s' % pkg, file=sys.stderr)
for req in reqs:
if not req.dist:
req_str = (
'{0} [required: {1}, '
'installed: <unknown>]'
).format(req.project_name, req.version_spec)
else:
req_str = req.render_as_branch(p, False)
req_str = req.render_as_branch(False)
print(' - %s' % req_str, file=sys.stderr)
print('-'*72, file=sys.stderr)

View File

@ -87,7 +87,7 @@ def test_ReqPackage_render_as_branch():
assert mks1.project_name == 'markupsafe'
assert mks1.installed_version == '0.18'
assert mks1.version_spec is None
assert mks1.render_as_branch(False) == 'markupsafe [installed: 0.18]'
assert mks1.render_as_branch(False) == 'markupsafe [required: None, installed: 0.18]'
assert mks1.render_as_branch(True) == 'MarkupSafe==0.18'
mks2 = find_req('markupsafe', 'mako')
assert mks2.project_name == 'MarkupSafe'
@ -126,7 +126,7 @@ def test_render_tree_freeze():
line = line.replace('origin/HEAD', 'master')
lines.add(line)
assert 'Flask-Script==0.6.6' in lines
assert ' SQLAlchemy==0.9.1' in lines
assert ' SQLAlchemy==0.9.1' in lines
# TODO! Fix the following failing test
# assert '-e git+https://github.com/naiquevin/lookupy.git@cdbe30c160e1c29802df75e145ea4ad903c05386#egg=Lookupy-master' in lines
assert 'itsdangerous==0.23' not in lines
@ -147,9 +147,9 @@ def test_render_tree_cyclic_dependency():
tree_str = render_tree(tree, list_all=True)
lines = set(tree_str.split('\n'))
assert 'CircularDependencyA==0.0.0' in lines
assert ' - CircularDependencyB [installed: 0.0.0]' in lines
assert ' - CircularDependencyB [required: None, installed: 0.0.0]' in lines
assert 'CircularDependencyB==0.0.0' in lines
assert ' - CircularDependencyA [installed: 0.0.0]' in lines
assert ' - CircularDependencyA [required: None, installed: 0.0.0]' in lines
def test_render_tree_freeze_cyclic_dependency():
@ -157,9 +157,9 @@ def test_render_tree_freeze_cyclic_dependency():
tree_str = render_tree(tree, list_all=True, frozen=True)
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 'CircularDependencyB==0.0.0' in lines
assert ' CircularDependencyA==0.0.0' in lines
assert ' CircularDependencyA==0.0.0' in lines
def test_conflicting_deps():