diff options
-rw-r--r-- | lib/spack/spack/spec.py | 23 | ||||
-rw-r--r-- | lib/spack/spack/test/spec_semantics.py | 95 |
2 files changed, 58 insertions, 60 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index ced35d5c7d..78530f7a7c 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -4115,18 +4115,10 @@ class Spec(object): clr.cwrite(f, stream=out, color=color) def write_attribute(spec, attribute, color): - current = spec - if attribute.startswith("^"): - attribute = attribute[1:] - dep, attribute = attribute.split(".", 1) - current = self[dep] - - if attribute == "": - raise SpecFormatStringError("Format string attributes must be non-empty") attribute = attribute.lower() sig = "" - if attribute[0] in "@%/": + if attribute.startswith(("@", "%", "/")): # color sigils that are inside braces sig = attribute[0] attribute = attribute[1:] @@ -4134,6 +4126,15 @@ class Spec(object): sig = " arch=" # include space as separator attribute = attribute[5:] + current = spec + if attribute.startswith("^"): + attribute = attribute[1:] + dep, attribute = attribute.split(".", 1) + current = self[dep] + + if attribute == "": + raise SpecFormatStringError("Format string attributes must be non-empty") + parts = attribute.split(".") assert parts @@ -4162,9 +4163,9 @@ class Spec(object): col = "#" if ":" in attribute: _, length = attribute.split(":") - write(sig + morph(spec, spec.dag_hash(int(length))), col) + write(sig + morph(spec, current.dag_hash(int(length))), col) else: - write(sig + morph(spec, spec.dag_hash()), col) + write(sig + morph(spec, current.dag_hash()), col) return # Iterate over components using getattr to get next element diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py index 63e2253b68..01a8de5e46 100644 --- a/lib/spack/spack/test/spec_semantics.py +++ b/lib/spack/spack/test/spec_semantics.py @@ -621,65 +621,62 @@ class TestSpecSemantics(object): # Testing named strings ie {string} and whether we get # the correct component # Mixed case intentional to test both + # Fields are as follow + # fmt_str: the format string to test + # sigil: the portion that is a sigil (may be empty string) + # prop: the property to get + # component: subcomponent of spec from which to get property package_segments = [ - ("{NAME}", "name"), - ("{VERSION}", "versions"), - ("{compiler}", "compiler"), - ("{compiler_flags}", "compiler_flags"), - ("{variants}", "variants"), - ("{architecture}", "architecture"), + ("{NAME}", "", "name", lambda spec: spec), + ("{VERSION}", "", "versions", lambda spec: spec), + ("{compiler}", "", "compiler", lambda spec: spec), + ("{compiler_flags}", "", "compiler_flags", lambda spec: spec), + ("{variants}", "", "variants", lambda spec: spec), + ("{architecture}", "", "architecture", lambda spec: spec), + ("{@VERSIONS}", "@", "version", lambda spec: spec), + ("{%compiler}", "%", "compiler", lambda spec: spec), + ("{arch=architecture}", "arch=", "architecture", lambda spec: spec), + ("{compiler.name}", "", "name", lambda spec: spec.compiler), + ("{compiler.version}", "", "versions", lambda spec: spec.compiler), + ("{%compiler.name}", "%", "name", lambda spec: spec.compiler), + ("{@compiler.version}", "@", "version", lambda spec: spec.compiler), + ("{architecture.platform}", "", "platform", lambda spec: spec.architecture), + ("{architecture.os}", "", "os", lambda spec: spec.architecture), + ("{architecture.target}", "", "target", lambda spec: spec.architecture), + ("{prefix}", "", "prefix", lambda spec: spec), ] - sigil_package_segments = [ - ("{@VERSIONS}", "@" + str(spec.version)), - ("{%compiler}", "%" + str(spec.compiler)), - ("{arch=architecture}", "arch=" + str(spec.architecture)), - ] - - compiler_segments = [("{compiler.name}", "name"), ("{compiler.version}", "versions")] - - sigil_compiler_segments = [ - ("{%compiler.name}", "%" + spec.compiler.name), - ("{@compiler.version}", "@" + str(spec.compiler.version)), - ] - - architecture_segments = [ - ("{architecture.platform}", "platform"), - ("{architecture.os}", "os"), - ("{architecture.target}", "target"), + hash_segments = [ + ("{hash:7}", "", lambda s: s.dag_hash(7)), + ("{/hash}", "/", lambda s: "/" + s.dag_hash()), ] other_segments = [ ("{spack_root}", spack.paths.spack_root), ("{spack_install}", spack.store.layout.root), - ("{hash:7}", spec.dag_hash(7)), - ("{/hash}", "/" + spec.dag_hash()), ] - for named_str, prop in package_segments: - expected = getattr(spec, prop, "") - actual = spec.format(named_str) - assert str(expected).strip() == actual - - for named_str, expected in sigil_package_segments: - actual = spec.format(named_str) - assert expected == actual - - compiler = spec.compiler - for named_str, prop in compiler_segments: - expected = getattr(compiler, prop, "") - actual = spec.format(named_str) - assert str(expected) == actual - - for named_str, expected in sigil_compiler_segments: - actual = spec.format(named_str) - assert expected == actual - - arch = spec.architecture - for named_str, prop in architecture_segments: - expected = getattr(arch, prop, "") - actual = spec.format(named_str) - assert str(expected) == actual + def depify(depname, fmt_str, sigil): + sig = len(sigil) + opening = fmt_str[: 1 + sig] + closing = fmt_str[1 + sig :] + return spec[depname], opening + f"^{depname}." + closing + + def check_prop(check_spec, fmt_str, prop, getter): + actual = spec.format(fmt_str) + expected = getter(check_spec) + assert actual == str(expected).strip() + + for named_str, sigil, prop, get_component in package_segments: + getter = lambda s: sigil + str(getattr(get_component(s), prop, "")) + check_prop(spec, named_str, prop, getter) + mpi, fmt_str = depify("mpi", named_str, sigil) + check_prop(mpi, fmt_str, prop, getter) + + for named_str, sigil, getter in hash_segments: + assert spec.format(named_str) == getter(spec) + callpath, fmt_str = depify("callpath", named_str, sigil) + assert spec.format(fmt_str) == getter(callpath) for named_str, expected in other_segments: actual = spec.format(named_str) |