possible_dependencies() now reports missing dependencies

- Add an optional argument so that `possible_dependencies()` will report
  missing dependencies.
- Add a test to ensure it works.
- Ignore missing dependencies in `possible_dependencies()` by default.
This commit is contained in:
Todd Gamblin 2019-12-02 01:01:11 -08:00
parent 81b147cc0a
commit 531f370e0d
3 changed files with 69 additions and 15 deletions

View File

@ -556,16 +556,19 @@ def installed_upstream(self):
@classmethod
def possible_dependencies(
cls, transitive=True, expand_virtuals=True, deptype='all',
visited=None):
visited=None, missing=None):
"""Return dict of possible dependencies of this package.
Args:
transitive (bool): return all transitive dependencies if True,
only direct dependencies if False.
expand_virtuals (bool): expand virtual dependencies into all
possible implementations.
deptype (str or tuple): dependency types to consider
visited (set): set of names of dependencies visited so far.
transitive (bool, optional): return all transitive dependencies if
True, only direct dependencies if False (default True)..
expand_virtuals (bool, optional): expand virtual dependencies into
all possible implementations (default True)
deptype (str or tuple, optional): dependency types to consider
visited (dicct, optional): dict of names of dependencies visited so
far, mapped to their immediate dependencies' names.
missing (dict, optional): dict to populate with packages and their
*missing* dependencies.
Returns:
(dict): dictionary mapping dependency names to *their*
@ -576,7 +579,12 @@ def possible_dependencies(
*immediate* dependencies. If ``expand_virtuals`` is ``False``,
virtual package names wil be inserted as keys mapped to empty
sets of dependencies. Virtuals, if not expanded, are treated as
though they have no immediate dependencies
though they have no immediate dependencies.
Missing dependencies by default are ignored, but if a
missing dict is provided, it will be populated with package names
mapped to any dependencies they have that are in no
repositories. This is only populated if transitive is True.
Note: the returned dict *includes* the package itself.
@ -586,6 +594,9 @@ def possible_dependencies(
if visited is None:
visited = {cls.name: set()}
if missing is None:
missing = {cls.name: set()}
for name, conditions in cls.dependencies.items():
# check whether this dependency could be of the type asked for
types = [dep.type for cond, dep in conditions.items()]
@ -609,12 +620,24 @@ def possible_dependencies(
# recursively traverse dependencies
for dep_name in dep_names:
if dep_name not in visited:
visited.setdefault(dep_name, set())
if transitive:
dep_cls = spack.repo.path.get_pkg_class(dep_name)
dep_cls.possible_dependencies(
transitive, expand_virtuals, deptype, visited)
if dep_name in visited:
continue
visited.setdefault(dep_name, set())
# skip the rest if not transitive
if not transitive:
continue
try:
dep_cls = spack.repo.path.get_pkg_class(dep_name)
except spack.repo.UnknownPackageError:
# log unknown packages
missing.setdefault(cls.name, set()).add(dep_name)
continue
dep_cls.possible_dependencies(
transitive, expand_virtuals, deptype, visited, missing)
return visited
@ -2671,6 +2694,7 @@ def possible_dependencies(*pkg_or_spec, **kwargs):
transitive = kwargs.get('transitive', True)
expand_virtuals = kwargs.get('expand_virtuals', True)
deptype = kwargs.get('deptype', 'all')
missing = kwargs.get('missing')
packages = []
for pos in pkg_or_spec:
@ -2686,7 +2710,7 @@ def possible_dependencies(*pkg_or_spec, **kwargs):
visited = {}
for pkg in packages:
pkg.possible_dependencies(
transitive, expand_virtuals, deptype, visited)
transitive, expand_virtuals, deptype, visited, missing)
return visited

View File

@ -47,6 +47,15 @@ def test_possible_dependencies(mock_packages, mpileaks_possible_deps):
}
def test_possible_dependencies_missing(mock_packages):
md = spack.repo.get("missing-dependency")
missing = {}
md.possible_dependencies(transitive=True, missing=missing)
assert missing["missing-dependency"] == set([
"this-is-a-missing-dependency"
])
def test_possible_dependencies_with_deptypes(mock_packages):
dtbuild1 = spack.repo.get('dtbuild1')

View File

@ -0,0 +1,21 @@
# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack import *
class MissingDependency(Package):
"""Package with a dependency that does not exist."""
homepage = "http://www.example.com"
url = "http://www.example.com/missing-dependency-1.0.tar.gz"
version('1.0', '0123456789abcdef0123456789abcdef')
# intentionally missing to test possible_dependencies()
depends_on("this-is-a-missing-dependency")
# this one is a "real" mock dependency
depends_on("a")