Skip to content

Commit

Permalink
Merge branch 'release-0.0.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
sinoroc committed Oct 23, 2019
2 parents 01ae844 + b266c18 commit b1ff164
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 19 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

.. Keep the current version number on line number 5
0.0.3
=====

2019-10-23

* Add 'reverse' to show tree of dependent projects


0.0.2
=====

Expand Down
7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ Usage

.. code::
usage: deptree [-h] [--version] [project [project ...]]
usage: deptree [-h] [--version] [-r] [project [project ...]]
positional arguments:
project
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-h, --help show this help message and exit
--version show program's version number and exit
-r, --reverse
Repositories
Expand Down
105 changes: 90 additions & 15 deletions src/deptree/_pkg_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,17 @@ def _display_missing(requirement, depth):
)


def _display(requirement, depth, seen):
def _display_missing_reverse(requirement, depth):
print(
"{}{} # MISSING".format(
' ' * INDENTATION * depth,
requirement.project_name,
),
)


def _display(requirement, chain):
depth = len(chain)
try:
distribution = pkg_resources.get_distribution(requirement)
except pkg_resources.VersionConflict:
Expand All @@ -66,7 +76,7 @@ def _display(requirement, depth, seen):
# https://github.com/pypa/setuptools/issues/1677
_display_missing(requirement, depth)
else:
if distribution.key in seen:
if distribution.key in chain:
_display_cyclic(distribution, requirement, depth)
else:
_display_good(distribution, requirement, depth)
Expand All @@ -78,33 +88,98 @@ def _display(requirement, depth, seen):
for dependency_requirement in dependencies:
_display(
dependency_requirement,
depth + 1,
seen + [requirement.key],
chain + [distribution.key],
)


def _get_top_level():
def _display_reverse(distributions, project_req, dependency_req, chain):
depth = len(chain)
try:
project_dist = pkg_resources.get_distribution(project_req)
except pkg_resources.DistributionNotFound:
_display_missing_reverse(project_req, depth)
else:
if project_dist.key in chain:
_display_cyclic(project_dist, dependency_req, depth)
else:
if dependency_req:
try:
pkg_resources.get_distribution(dependency_req)
except pkg_resources.VersionConflict:
_display_conflict(project_dist, dependency_req, depth)
else:
_display_good(project_dist, dependency_req, depth)
else:
_display_good(project_dist, '', depth)
dependents = distributions[project_dist.key]['dependents']
for (dependent_key, dependent_req) in dependents.items():
_display_reverse(
distributions,
dependent_key,
dependent_req,
chain + [project_dist.key],
)


def _discover_distributions():
working_set = pkg_resources.working_set
distributions = {}
for distribution in working_set: # pylint: disable=not-an-iterable
distributions.setdefault(distribution.key, True) # if not exist yet
for dependency in distribution.requires():
distributions[dependency.key] = False
top_level = [
key = distribution.key
if key not in distributions:
distributions[key] = {
'dependencies': {},
'dependents': {},
}
distributions[key]['installed'] = True
for requirement in distribution.requires(extras=distribution.extras):
distributions[key]['dependencies'][requirement.key] = requirement
if requirement.key not in distributions:
distributions[requirement.key] = {
'dependencies': {},
'dependents': {},
'installed': False,
}
distributions[requirement.key]['dependents'][key] = requirement
return distributions


def _select_top_level(distributions):
selection = [
key
for (key, is_top_level) in distributions.items()
if is_top_level
for (key, info)
in distributions.items()
if not info['dependents']
]
return top_level
return selection


def main(selection):
def _select_bottom_level(distributions):
selection = [
key
for (key, info)
in distributions.items()
if info['installed'] and not info['dependencies']
]
return selection


def main(selection, reverse):
""" Main function """
distributions = None
if not selection or reverse:
distributions = _discover_distributions()
if not selection:
selection = _get_top_level()
if reverse:
selection = _select_bottom_level(distributions)
else:
selection = _select_top_level(distributions)
for item in selection:
requirement = pkg_resources.Requirement.parse(item)
_display(requirement, 0, [])
if reverse:
_display_reverse(distributions, requirement, None, [])
else:
_display(requirement, [])


# EOF
3 changes: 2 additions & 1 deletion src/deptree/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ def main():
allow_abbrev=False,
)
parser.add_argument('--version', action='version', version=_meta.VERSION)
parser.add_argument('-r', '--reverse', action='store_true')
parser.add_argument('selected_projects', metavar='project', nargs='*')
args = parser.parse_args()
_pkg_resources.main(args.selected_projects)
_pkg_resources.main(args.selected_projects, args.reverse)


# EOF

0 comments on commit b1ff164

Please sign in to comment.