diff options
Diffstat (limited to 'lib/spack/spack/test/spec_syntax.py')
-rw-r--r-- | lib/spack/spack/test/spec_syntax.py | 620 |
1 files changed, 311 insertions, 309 deletions
diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py index 815a46847a..956593ceb8 100644 --- a/lib/spack/spack/test/spec_syntax.py +++ b/lib/spack/spack/test/spec_syntax.py @@ -32,57 +32,61 @@ from spack.spec import ( from spack.variant import DuplicateVariantError # Sample output for a complex lexing. -complex_lex = [Token(sp.ID, 'mvapich_foo'), - Token(sp.DEP), - Token(sp.ID, '_openmpi'), - Token(sp.AT), - Token(sp.ID, '1.2'), - Token(sp.COLON), - Token(sp.ID, '1.4'), - Token(sp.COMMA), - Token(sp.ID, '1.6'), - Token(sp.PCT), - Token(sp.ID, 'intel'), - Token(sp.AT), - Token(sp.ID, '12.1'), - Token(sp.COLON), - Token(sp.ID, '12.6'), - Token(sp.ON), - Token(sp.ID, 'debug'), - Token(sp.OFF), - Token(sp.ID, 'qt_4'), - Token(sp.DEP), - Token(sp.ID, 'stackwalker'), - Token(sp.AT), - Token(sp.ID, '8.1_1e')] +complex_lex = [ + Token(sp.ID, "mvapich_foo"), + Token(sp.DEP), + Token(sp.ID, "_openmpi"), + Token(sp.AT), + Token(sp.ID, "1.2"), + Token(sp.COLON), + Token(sp.ID, "1.4"), + Token(sp.COMMA), + Token(sp.ID, "1.6"), + Token(sp.PCT), + Token(sp.ID, "intel"), + Token(sp.AT), + Token(sp.ID, "12.1"), + Token(sp.COLON), + Token(sp.ID, "12.6"), + Token(sp.ON), + Token(sp.ID, "debug"), + Token(sp.OFF), + Token(sp.ID, "qt_4"), + Token(sp.DEP), + Token(sp.ID, "stackwalker"), + Token(sp.AT), + Token(sp.ID, "8.1_1e"), +] # Another sample lexer output with a kv pair. -kv_lex = [Token(sp.ID, 'mvapich_foo'), - Token(sp.ID, 'debug'), - Token(sp.EQ), - Token(sp.VAL, '4'), - Token(sp.DEP), - Token(sp.ID, '_openmpi'), - Token(sp.AT), - Token(sp.ID, '1.2'), - Token(sp.COLON), - Token(sp.ID, '1.4'), - Token(sp.COMMA), - Token(sp.ID, '1.6'), - Token(sp.PCT), - Token(sp.ID, 'intel'), - Token(sp.AT), - Token(sp.ID, '12.1'), - Token(sp.COLON), - Token(sp.ID, '12.6'), - Token(sp.ON), - Token(sp.ID, 'debug'), - Token(sp.OFF), - Token(sp.ID, 'qt_4'), - Token(sp.DEP), - Token(sp.ID, 'stackwalker'), - Token(sp.AT), - Token(sp.ID, '8.1_1e')] +kv_lex = [ + Token(sp.ID, "mvapich_foo"), + Token(sp.ID, "debug"), + Token(sp.EQ), + Token(sp.VAL, "4"), + Token(sp.DEP), + Token(sp.ID, "_openmpi"), + Token(sp.AT), + Token(sp.ID, "1.2"), + Token(sp.COLON), + Token(sp.ID, "1.4"), + Token(sp.COMMA), + Token(sp.ID, "1.6"), + Token(sp.PCT), + Token(sp.ID, "intel"), + Token(sp.AT), + Token(sp.ID, "12.1"), + Token(sp.COLON), + Token(sp.ID, "12.6"), + Token(sp.ON), + Token(sp.ID, "debug"), + Token(sp.OFF), + Token(sp.ID, "qt_4"), + Token(sp.DEP), + Token(sp.ID, "stackwalker"), + Token(sp.AT), + Token(sp.ID, "8.1_1e"), +] class TestSpecSyntax(object): @@ -93,21 +97,21 @@ class TestSpecSyntax(object): def check_parse(self, expected, spec=None): """Assert that the provided spec is able to be parsed. - If this is called with one argument, it assumes that the - string is canonical (i.e., no spaces and ~ instead of - for - variants) and that it will convert back to the string it came - from. + If this is called with one argument, it assumes that the + string is canonical (i.e., no spaces and ~ instead of - for + variants) and that it will convert back to the string it came + from. - If this is called with two arguments, the first argument is - the expected canonical form and the second is a non-canonical - input to be parsed. + If this is called with two arguments, the first argument is + the expected canonical form and the second is a non-canonical + input to be parsed. """ if spec is None: spec = expected output = sp.parse(spec) - parsed = (" ".join(str(spec) for spec in output)) + parsed = " ".join(str(spec) for spec in output) assert expected == parsed def check_lex(self, tokens, spec): @@ -141,23 +145,22 @@ class TestSpecSyntax(object): self.check_parse("^zlib") self.check_parse("+foo") self.check_parse("arch=test-None-None", "platform=test") - self.check_parse('@2.7:') + self.check_parse("@2.7:") def test_anonymous_specs_with_multiple_parts(self): # Parse anonymous spec with multiple tokens - self.check_parse('@4.2: languages=go', 'languages=go @4.2:') - self.check_parse('@4.2: languages=go') + self.check_parse("@4.2: languages=go", "languages=go @4.2:") + self.check_parse("@4.2: languages=go") def test_simple_dependence(self): self.check_parse("openmpi ^hwloc") self.check_parse("openmpi ^hwloc", "openmpi^hwloc") self.check_parse("openmpi ^hwloc ^libunwind") - self.check_parse("openmpi ^hwloc ^libunwind", - "openmpi^hwloc^libunwind") + self.check_parse("openmpi ^hwloc ^libunwind", "openmpi^hwloc^libunwind") def test_version_after_compiler(self): - self.check_parse('foo@2.0%bar@1.0', 'foo %bar@1.0 @2.0') + self.check_parse("foo@2.0%bar@1.0", "foo %bar@1.0 @2.0") def test_dependencies_with_versions(self): self.check_parse("openmpi ^hwloc@1.2e6") @@ -170,120 +173,103 @@ class TestSpecSyntax(object): def test_multiple_specs_after_kv(self): self.check_parse('mvapich cppflags="-O3 -fPIC" emacs') - self.check_parse('mvapich cflags="-O3" emacs', - 'mvapich cflags=-O3 emacs') + self.check_parse('mvapich cflags="-O3" emacs', "mvapich cflags=-O3 emacs") def test_multiple_specs_long_second(self): - self.check_parse('mvapich emacs@1.1.1%intel cflags="-O3"', - 'mvapich emacs @1.1.1 %intel cflags=-O3') + self.check_parse( + 'mvapich emacs@1.1.1%intel cflags="-O3"', "mvapich emacs @1.1.1 %intel cflags=-O3" + ) self.check_parse('mvapich cflags="-O3 -fPIC" emacs ^ncurses%intel') - self.check_parse('mvapich cflags="-O3 -fPIC" emacs ^ncurses%intel', - 'mvapich cflags="-O3 -fPIC" emacs^ncurses%intel') + self.check_parse( + 'mvapich cflags="-O3 -fPIC" emacs ^ncurses%intel', + 'mvapich cflags="-O3 -fPIC" emacs^ncurses%intel', + ) def test_full_specs(self): self.check_parse( - "mvapich_foo" - " ^_openmpi@1.2:1.4,1.6%intel@12.1+debug~qt_4" - " ^stackwalker@8.1_1e") + "mvapich_foo" " ^_openmpi@1.2:1.4,1.6%intel@12.1+debug~qt_4" " ^stackwalker@8.1_1e" + ) self.check_parse( - "mvapich_foo" - " ^_openmpi@1.2:1.4,1.6%intel@12.1~qt_4 debug=2" - " ^stackwalker@8.1_1e") + "mvapich_foo" " ^_openmpi@1.2:1.4,1.6%intel@12.1~qt_4 debug=2" " ^stackwalker@8.1_1e" + ) self.check_parse( - 'mvapich_foo' + "mvapich_foo" ' ^_openmpi@1.2:1.4,1.6%intel@12.1 cppflags="-O3" +debug~qt_4' - ' ^stackwalker@8.1_1e') + " ^stackwalker@8.1_1e" + ) self.check_parse( "mvapich_foo" " ^_openmpi@1.2:1.4,1.6%intel@12.1~qt_4 debug=2" - " ^stackwalker@8.1_1e arch=test-redhat6-x86") + " ^stackwalker@8.1_1e arch=test-redhat6-x86" + ) def test_yaml_specs(self): - self.check_parse( - "yaml-cpp@0.1.8%intel@12.1" - " ^boost@3.1.4") + self.check_parse("yaml-cpp@0.1.8%intel@12.1" " ^boost@3.1.4") tempspec = r"builtin.yaml-cpp%gcc" - self.check_parse( - tempspec.strip("builtin."), - spec=tempspec) + self.check_parse(tempspec.strip("builtin."), spec=tempspec) tempspec = r"testrepo.yaml-cpp%gcc" - self.check_parse( - tempspec.strip("testrepo."), - spec=tempspec) + self.check_parse(tempspec.strip("testrepo."), spec=tempspec) tempspec = r"builtin.yaml-cpp@0.1.8%gcc" - self.check_parse( - tempspec.strip("builtin."), - spec=tempspec) + self.check_parse(tempspec.strip("builtin."), spec=tempspec) tempspec = r"builtin.yaml-cpp@0.1.8%gcc@7.2.0" - self.check_parse( - tempspec.strip("builtin."), - spec=tempspec) - tempspec = r"builtin.yaml-cpp@0.1.8%gcc@7.2.0" \ - r" ^boost@3.1.4" - self.check_parse( - tempspec.strip("builtin."), - spec=tempspec) + self.check_parse(tempspec.strip("builtin."), spec=tempspec) + tempspec = r"builtin.yaml-cpp@0.1.8%gcc@7.2.0" r" ^boost@3.1.4" + self.check_parse(tempspec.strip("builtin."), spec=tempspec) def test_canonicalize(self): self.check_parse( "mvapich_foo" " ^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug~qt_4" " ^stackwalker@8.1_1e", - "mvapich_foo " "^_openmpi@1.6,1.2:1.4%intel@12.1:12.6+debug~qt_4 " - "^stackwalker@8.1_1e") + "^stackwalker@8.1_1e", + ) self.check_parse( "mvapich_foo" " ^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug~qt_4" " ^stackwalker@8.1_1e", - "mvapich_foo " "^stackwalker@8.1_1e " - "^_openmpi@1.6,1.2:1.4%intel@12.1:12.6~qt_4+debug") + "^_openmpi@1.6,1.2:1.4%intel@12.1:12.6~qt_4+debug", + ) self.check_parse( - "x ^y@1,2:3,4%intel@1,2,3,4+a~b+c~d+e~f", - "x ^y~f+e~d+c~b+a@4,2:3,1%intel@4,3,2,1") + "x ^y@1,2:3,4%intel@1,2,3,4+a~b+c~d+e~f", "x ^y~f+e~d+c~b+a@4,2:3,1%intel@4,3,2,1" + ) self.check_parse( - "x arch=test-redhat6-None" - " ^y arch=test-None-core2" - " ^z arch=linux-None-None", - - "x os=fe " - "^y target=be " - "^z platform=linux") + "x arch=test-redhat6-None" " ^y arch=test-None-core2" " ^z arch=linux-None-None", + "x os=fe " "^y target=be " "^z platform=linux", + ) self.check_parse( - "x arch=test-debian6-core2" - " ^y arch=test-debian6-core2", - - "x os=default_os target=default_target" - " ^y os=default_os target=default_target") + "x arch=test-debian6-core2" " ^y arch=test-debian6-core2", + "x os=default_os target=default_target" " ^y os=default_os target=default_target", + ) self.check_parse("x ^y", "x@: ^y@:") def test_parse_errors(self): - errors = ['x@@1.2', 'x ^y@@1.2', 'x@1.2::', 'x::'] + errors = ["x@@1.2", "x ^y@@1.2", "x@1.2::", "x::"] self._check_raises(SpecParseError, errors) def _check_hash_parse(self, spec): """Check several ways to specify a spec by hash.""" # full hash - self.check_parse(str(spec), '/' + spec.dag_hash()) + self.check_parse(str(spec), "/" + spec.dag_hash()) # partial hash - self.check_parse(str(spec), '/ ' + spec.dag_hash()[:5]) + self.check_parse(str(spec), "/ " + spec.dag_hash()[:5]) # name + hash - self.check_parse(str(spec), spec.name + '/' + spec.dag_hash()) + self.check_parse(str(spec), spec.name + "/" + spec.dag_hash()) # name + version + space + partial hash self.check_parse( - str(spec), spec.name + '@' + str(spec.version) + - ' /' + spec.dag_hash()[:6]) + str(spec), spec.name + "@" + str(spec.version) + " /" + spec.dag_hash()[:6] + ) @pytest.mark.db def test_spec_by_hash(self, database): @@ -295,100 +281,103 @@ class TestSpecSyntax(object): @pytest.mark.db def test_dep_spec_by_hash(self, database): - mpileaks_zmpi = database.query_one('mpileaks ^zmpi') - zmpi = database.query_one('zmpi') - fake = database.query_one('fake') + mpileaks_zmpi = database.query_one("mpileaks ^zmpi") + zmpi = database.query_one("zmpi") + fake = database.query_one("fake") - assert 'fake' in mpileaks_zmpi - assert 'zmpi' in mpileaks_zmpi + assert "fake" in mpileaks_zmpi + assert "zmpi" in mpileaks_zmpi - mpileaks_hash_fake = sp.Spec('mpileaks ^/' + fake.dag_hash()) - assert 'fake' in mpileaks_hash_fake - assert mpileaks_hash_fake['fake'] == fake + mpileaks_hash_fake = sp.Spec("mpileaks ^/" + fake.dag_hash()) + assert "fake" in mpileaks_hash_fake + assert mpileaks_hash_fake["fake"] == fake mpileaks_hash_zmpi = sp.Spec( - 'mpileaks %' + str(mpileaks_zmpi.compiler) + - ' ^ / ' + zmpi.dag_hash()) - assert 'zmpi' in mpileaks_hash_zmpi - assert mpileaks_hash_zmpi['zmpi'] == zmpi + "mpileaks %" + str(mpileaks_zmpi.compiler) + " ^ / " + zmpi.dag_hash() + ) + assert "zmpi" in mpileaks_hash_zmpi + assert mpileaks_hash_zmpi["zmpi"] == zmpi assert mpileaks_hash_zmpi.compiler == mpileaks_zmpi.compiler mpileaks_hash_fake_and_zmpi = sp.Spec( - 'mpileaks ^/' + fake.dag_hash()[:4] + '^ / ' + zmpi.dag_hash()[:5]) - assert 'zmpi' in mpileaks_hash_fake_and_zmpi - assert mpileaks_hash_fake_and_zmpi['zmpi'] == zmpi + "mpileaks ^/" + fake.dag_hash()[:4] + "^ / " + zmpi.dag_hash()[:5] + ) + assert "zmpi" in mpileaks_hash_fake_and_zmpi + assert mpileaks_hash_fake_and_zmpi["zmpi"] == zmpi - assert 'fake' in mpileaks_hash_fake_and_zmpi - assert mpileaks_hash_fake_and_zmpi['fake'] == fake + assert "fake" in mpileaks_hash_fake_and_zmpi + assert mpileaks_hash_fake_and_zmpi["fake"] == fake @pytest.mark.db def test_multiple_specs_with_hash(self, database): - mpileaks_zmpi = database.query_one('mpileaks ^zmpi') - callpath_mpich2 = database.query_one('callpath ^mpich2') + mpileaks_zmpi = database.query_one("mpileaks ^zmpi") + callpath_mpich2 = database.query_one("callpath ^mpich2") # name + hash + separate hash - specs = sp.parse('mpileaks /' + mpileaks_zmpi.dag_hash() + - '/' + callpath_mpich2.dag_hash()) + specs = sp.parse( + "mpileaks /" + mpileaks_zmpi.dag_hash() + "/" + callpath_mpich2.dag_hash() + ) assert len(specs) == 2 # 2 separate hashes - specs = sp.parse('/' + mpileaks_zmpi.dag_hash() + - '/' + callpath_mpich2.dag_hash()) + specs = sp.parse("/" + mpileaks_zmpi.dag_hash() + "/" + callpath_mpich2.dag_hash()) assert len(specs) == 2 # 2 separate hashes + name - specs = sp.parse('/' + mpileaks_zmpi.dag_hash() + - '/' + callpath_mpich2.dag_hash() + - ' callpath') + specs = sp.parse( + "/" + mpileaks_zmpi.dag_hash() + "/" + callpath_mpich2.dag_hash() + " callpath" + ) assert len(specs) == 3 # hash + 2 names - specs = sp.parse('/' + mpileaks_zmpi.dag_hash() + - ' callpath' + - ' callpath') + specs = sp.parse("/" + mpileaks_zmpi.dag_hash() + " callpath" + " callpath") assert len(specs) == 3 # hash + name + hash - specs = sp.parse('/' + mpileaks_zmpi.dag_hash() + - ' callpath' + - ' / ' + callpath_mpich2.dag_hash()) + specs = sp.parse( + "/" + mpileaks_zmpi.dag_hash() + " callpath" + " / " + callpath_mpich2.dag_hash() + ) assert len(specs) == 2 @pytest.mark.db def test_ambiguous_hash(self, mutable_database): - x1 = Spec('a') + x1 = Spec("a") x1.concretize() - x1._hash = 'xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy' - x2 = Spec('a') + x1._hash = "xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" + x2 = Spec("a") x2.concretize() - x2._hash = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + x2._hash = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" mutable_database.add(x1, spack.store.layout) mutable_database.add(x2, spack.store.layout) # ambiguity in first hash character - self._check_raises(AmbiguousHashError, ['/x']) + self._check_raises(AmbiguousHashError, ["/x"]) # ambiguity in first hash character AND spec name - self._check_raises(AmbiguousHashError, ['a/x']) + self._check_raises(AmbiguousHashError, ["a/x"]) @pytest.mark.db def test_invalid_hash(self, database): - mpileaks_zmpi = database.query_one('mpileaks ^zmpi') - zmpi = database.query_one('zmpi') + mpileaks_zmpi = database.query_one("mpileaks ^zmpi") + zmpi = database.query_one("zmpi") - mpileaks_mpich = database.query_one('mpileaks ^mpich') - mpich = database.query_one('mpich') + mpileaks_mpich = database.query_one("mpileaks ^mpich") + mpich = database.query_one("mpich") # name + incompatible hash - self._check_raises(InvalidHashError, [ - 'zmpi /' + mpich.dag_hash(), - 'mpich /' + zmpi.dag_hash()]) + self._check_raises( + InvalidHashError, ["zmpi /" + mpich.dag_hash(), "mpich /" + zmpi.dag_hash()] + ) # name + dep + incompatible hash - self._check_raises(InvalidHashError, [ - 'mpileaks ^mpich /' + mpileaks_zmpi.dag_hash(), - 'mpileaks ^zmpi /' + mpileaks_mpich.dag_hash()]) + self._check_raises( + InvalidHashError, + [ + "mpileaks ^mpich /" + mpileaks_zmpi.dag_hash(), + "mpileaks ^zmpi /" + mpileaks_mpich.dag_hash(), + ], + ) @pytest.mark.db def test_nonexistent_hash(self, database): @@ -396,13 +385,11 @@ class TestSpecSyntax(object): specs = database.query() # This hash shouldn't be in the test DB. What are the odds :) - no_such_hash = 'aaaaaaaaaaaaaaa' + no_such_hash = "aaaaaaaaaaaaaaa" hashes = [s._hash for s in specs] - assert no_such_hash not in [h[:len(no_such_hash)] for h in hashes] + assert no_such_hash not in [h[: len(no_such_hash)] for h in hashes] - self._check_raises(NoSuchHashError, [ - '/' + no_such_hash, - 'mpileaks /' + no_such_hash]) + self._check_raises(NoSuchHashError, ["/" + no_such_hash, "mpileaks /" + no_such_hash]) @pytest.mark.db def test_redundant_spec(self, database): @@ -412,45 +399,42 @@ class TestSpecSyntax(object): specs only raise errors if constraints cause a contradiction? """ - mpileaks_zmpi = database.query_one('mpileaks ^zmpi') - callpath_zmpi = database.query_one('callpath ^zmpi') - dyninst = database.query_one('dyninst') + mpileaks_zmpi = database.query_one("mpileaks ^zmpi") + callpath_zmpi = database.query_one("callpath ^zmpi") + dyninst = database.query_one("dyninst") - mpileaks_mpich2 = database.query_one('mpileaks ^mpich2') + mpileaks_mpich2 = database.query_one("mpileaks ^mpich2") redundant_specs = [ # redudant compiler - '/' + mpileaks_zmpi.dag_hash() + '%' + str(mpileaks_zmpi.compiler), - + "/" + mpileaks_zmpi.dag_hash() + "%" + str(mpileaks_zmpi.compiler), # redudant version - 'mpileaks/' + mpileaks_mpich2.dag_hash() + - '@' + str(mpileaks_mpich2.version), - + "mpileaks/" + mpileaks_mpich2.dag_hash() + "@" + str(mpileaks_mpich2.version), # redundant dependency - 'callpath /' + callpath_zmpi.dag_hash() + '^ libelf', - + "callpath /" + callpath_zmpi.dag_hash() + "^ libelf", # redundant flags - '/' + dyninst.dag_hash() + ' cflags="-O3 -fPIC"'] + "/" + dyninst.dag_hash() + ' cflags="-O3 -fPIC"', + ] self._check_raises(RedundantSpecError, redundant_specs) def test_duplicate_variant(self): duplicates = [ - 'x@1.2+debug+debug', - 'x ^y@1.2+debug debug=true', - 'x ^y@1.2 debug=false debug=true', - 'x ^y@1.2 debug=false ~debug' + "x@1.2+debug+debug", + "x ^y@1.2+debug debug=true", + "x ^y@1.2 debug=false debug=true", + "x ^y@1.2 debug=false ~debug", ] self._check_raises(DuplicateVariantError, duplicates) def test_multiple_versions(self): multiples = [ - 'x@1.2@2.3', - 'x@1.2:2.3@1.4', - 'x@1.2@2.3:2.4', - 'x@1.2@2.3,2.4', - 'x@1.2 +foo~bar @2.3', - 'x@1.2%y@1.2@2.3:2.4', + "x@1.2@2.3", + "x@1.2:2.3@1.4", + "x@1.2@2.3:2.4", + "x@1.2@2.3,2.4", + "x@1.2 +foo~bar @2.3", + "x@1.2%y@1.2@2.3:2.4", ] self._check_raises(MultipleVersionError, multiples) @@ -464,7 +448,7 @@ class TestSpecSyntax(object): "x%gcc%intel", "x ^y%intel%intel", "x ^y%intel%gcc", - "x ^y%gcc%intel" + "x ^y%gcc%intel", ] self._check_raises(DuplicateCompilerSpecError, duplicates) @@ -474,7 +458,7 @@ class TestSpecSyntax(object): "x arch=linux-rhel7-x86_64 arch=linux-rhel7-ppc64le", "x arch=linux-rhel7-ppc64le arch=linux-rhel7-x86_64", "y ^x arch=linux-rhel7-x86_64 arch=linux-rhel7-x86_64", - "y ^x arch=linux-rhel7-x86_64 arch=linux-rhel7-ppc64le" + "y ^x arch=linux-rhel7-x86_64 arch=linux-rhel7-ppc64le", ] self._check_raises(DuplicateArchitectureError, duplicates) @@ -486,18 +470,18 @@ class TestSpecSyntax(object): "x target=fe target=be", "x platform=test platform=test", "x os=fe platform=test target=fe os=fe", - "x target=be platform=test os=be os=fe" + "x target=be platform=test os=be os=fe", ] self._check_raises(DuplicateArchitectureError, duplicates) - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_simple(self, mock_packages, tmpdir): - s = Spec('libdwarf') + s = Spec("libdwarf") s.concretize() - specfile = tmpdir.join('libdwarf.yaml') + specfile = tmpdir.join("libdwarf.yaml") - with specfile.open('w') as f: + with specfile.open("w") as f: f.write(s.to_yaml(hash=ht.dag_hash)) # Check an absolute path to spec.yaml by itself: @@ -507,20 +491,20 @@ class TestSpecSyntax(object): # Check absolute path to spec.yaml mixed with a clispec, e.g.: # "spack spec mvapich_foo /path/to/libdwarf.yaml" - specs = sp.parse('mvapich_foo {0}'.format(specfile.strpath)) + specs = sp.parse("mvapich_foo {0}".format(specfile.strpath)) assert len(specs) == 2 - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_filename_missing_slash_as_spec(self, mock_packages, tmpdir): """Ensure that libelf.yaml parses as a spec, NOT a file.""" - s = Spec('libelf') + s = Spec("libelf") s.concretize() - specfile = tmpdir.join('libelf.yaml') + specfile = tmpdir.join("libelf.yaml") # write the file to the current directory to make sure it exists, # and that we still do not parse the spec as a file. - with specfile.open('w') as f: + with specfile.open("w") as f: f.write(s.to_yaml(hash=ht.dag_hash)) # Check the spec `libelf.yaml` in the working directory, which @@ -540,42 +524,41 @@ class TestSpecSyntax(object): with pytest.raises(spack.repo.UnknownPackageError) as exc_info: spec.concretize() assert exc_info.value.long_message - assert ("Did you mean to specify a filename with './libelf.yaml'?" - in exc_info.value.long_message) + assert ( + "Did you mean to specify a filename with './libelf.yaml'?" + in exc_info.value.long_message + ) # make sure that only happens when the spec ends in yaml with pytest.raises(spack.repo.UnknownPackageError) as exc_info: - Spec('builtin.mock.doesnotexist').concretize() - assert ( - not exc_info.value.long_message or ( - "Did you mean to specify a filename with" not in - exc_info.value.long_message - ) + Spec("builtin.mock.doesnotexist").concretize() + assert not exc_info.value.long_message or ( + "Did you mean to specify a filename with" not in exc_info.value.long_message ) - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_dependency(self, mock_packages, tmpdir): - s = Spec('libdwarf') + s = Spec("libdwarf") s.concretize() - specfile = tmpdir.join('libelf.yaml') + specfile = tmpdir.join("libelf.yaml") - with specfile.open('w') as f: - f.write(s['libelf'].to_yaml(hash=ht.dag_hash)) + with specfile.open("w") as f: + f.write(s["libelf"].to_yaml(hash=ht.dag_hash)) # Make sure we can use yaml path as dependency, e.g.: # "spack spec libdwarf ^ /path/to/libelf.yaml" - specs = sp.parse('libdwarf ^ {0}'.format(specfile.strpath)) + specs = sp.parse("libdwarf ^ {0}".format(specfile.strpath)) assert len(specs) == 1 - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_relative_paths(self, mock_packages, tmpdir): - s = Spec('libdwarf') + s = Spec("libdwarf") s.concretize() - specfile = tmpdir.join('libdwarf.yaml') + specfile = tmpdir.join("libdwarf.yaml") - with specfile.open('w') as f: + with specfile.open("w") as f: f.write(s.to_yaml(hash=ht.dag_hash)) file_name = specfile.basename @@ -586,52 +569,51 @@ class TestSpecSyntax(object): # Test for command like: "spack spec libelf.yaml" # This should parse a single spec, but should not concretize. # See test_parse_filename_missing_slash_as_spec() - specs = sp.parse('{0}'.format(file_name)) + specs = sp.parse("{0}".format(file_name)) assert len(specs) == 1 # Make sure this also works: "spack spec ./libelf.yaml" - specs = sp.parse('./{0}'.format(file_name)) + specs = sp.parse("./{0}".format(file_name)) assert len(specs) == 1 # Should also be accepted: "spack spec ../<cur-dir>/libelf.yaml" - specs = sp.parse('../{0}/{1}'.format(parent_dir, file_name)) + specs = sp.parse("../{0}/{1}".format(parent_dir, file_name)) assert len(specs) == 1 # Should also handle mixed clispecs and relative paths, e.g.: # "spack spec mvapich_foo ../<cur-dir>/libelf.yaml" - specs = sp.parse('mvapich_foo ../{0}/{1}'.format( - parent_dir, file_name)) + specs = sp.parse("mvapich_foo ../{0}/{1}".format(parent_dir, file_name)) assert len(specs) == 2 - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_relative_subdir_path(self, mock_packages, tmpdir): - s = Spec('libdwarf') + s = Spec("libdwarf") s.concretize() - specfile = tmpdir.mkdir('subdir').join('libdwarf.yaml') + specfile = tmpdir.mkdir("subdir").join("libdwarf.yaml") - with specfile.open('w') as f: + with specfile.open("w") as f: f.write(s.to_yaml(hash=ht.dag_hash)) file_name = specfile.basename # Relative path to specfile with tmpdir.as_cwd(): - assert os.path.exists('subdir/{0}'.format(file_name)) + assert os.path.exists("subdir/{0}".format(file_name)) # Test for command like: "spack spec libelf.yaml" - specs = sp.parse('subdir/{0}'.format(file_name)) + specs = sp.parse("subdir/{0}".format(file_name)) assert len(specs) == 1 - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_dependency_relative_paths(self, mock_packages, tmpdir): - s = Spec('libdwarf') + s = Spec("libdwarf") s.concretize() - specfile = tmpdir.join('libelf.yaml') + specfile = tmpdir.join("libelf.yaml") - with specfile.open('w') as f: - f.write(s['libelf'].to_yaml(hash=ht.dag_hash)) + with specfile.open("w") as f: + f.write(s["libelf"].to_yaml(hash=ht.dag_hash)) file_name = specfile.basename parent_dir = os.path.basename(specfile.dirname) @@ -639,62 +621,67 @@ class TestSpecSyntax(object): # Relative path to specfile with fs.working_dir(specfile.dirname): # Test for command like: "spack spec libelf.yaml" - specs = sp.parse('libdwarf^{0}'.format(file_name)) + specs = sp.parse("libdwarf^{0}".format(file_name)) assert len(specs) == 1 # Make sure this also works: "spack spec ./libelf.yaml" - specs = sp.parse('libdwarf^./{0}'.format(file_name)) + specs = sp.parse("libdwarf^./{0}".format(file_name)) assert len(specs) == 1 # Should also be accepted: "spack spec ../<cur-dir>/libelf.yaml" - specs = sp.parse('libdwarf^../{0}/{1}'.format( - parent_dir, file_name)) + specs = sp.parse("libdwarf^../{0}/{1}".format(parent_dir, file_name)) assert len(specs) == 1 def test_parse_yaml_error_handling(self): - self._check_raises(NoSuchSpecFileError, [ - # Single spec that looks like a yaml path - '/bogus/path/libdwarf.yaml', - '../../libdwarf.yaml', - './libdwarf.yaml', - # Dependency spec that looks like a yaml path - 'libdwarf^/bogus/path/libelf.yaml', - 'libdwarf ^../../libelf.yaml', - 'libdwarf^ ./libelf.yaml', - # Multiple specs, one looks like a yaml path - 'mvapich_foo /bogus/path/libelf.yaml', - 'mvapich_foo ../../libelf.yaml', - 'mvapich_foo ./libelf.yaml', - ]) + self._check_raises( + NoSuchSpecFileError, + [ + # Single spec that looks like a yaml path + "/bogus/path/libdwarf.yaml", + "../../libdwarf.yaml", + "./libdwarf.yaml", + # Dependency spec that looks like a yaml path + "libdwarf^/bogus/path/libelf.yaml", + "libdwarf ^../../libelf.yaml", + "libdwarf^ ./libelf.yaml", + # Multiple specs, one looks like a yaml path + "mvapich_foo /bogus/path/libelf.yaml", + "mvapich_foo ../../libelf.yaml", + "mvapich_foo ./libelf.yaml", + ], + ) def test_nice_error_for_no_space_after_spec_filename(self): """Ensure that omitted spaces don't give weird errors about hashes.""" - self._check_raises(SpecFilenameError, [ - '/bogus/path/libdwarf.yamlfoobar', - 'libdwarf^/bogus/path/libelf.yamlfoobar ^/path/to/bogus.yaml', - ]) + self._check_raises( + SpecFilenameError, + [ + "/bogus/path/libdwarf.yamlfoobar", + "libdwarf^/bogus/path/libelf.yamlfoobar ^/path/to/bogus.yaml", + ], + ) - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_yaml_spec_not_filename(self, mock_packages, tmpdir): with pytest.raises(spack.repo.UnknownPackageError): - Spec('builtin.mock.yaml').concretize() + Spec("builtin.mock.yaml").concretize() with pytest.raises(spack.repo.UnknownPackageError): - Spec('builtin.mock.yamlfoobar').concretize() + Spec("builtin.mock.yamlfoobar").concretize() - @pytest.mark.usefixtures('config') + @pytest.mark.usefixtures("config") def test_parse_yaml_variant_error(self, mock_packages, tmpdir): - s = Spec('a') + s = Spec("a") s.concretize() - specfile = tmpdir.join('a.yaml') + specfile = tmpdir.join("a.yaml") - with specfile.open('w') as f: + with specfile.open("w") as f: f.write(s.to_yaml(hash=ht.dag_hash)) with pytest.raises(RedundantSpecError): # Trying to change a variant on a concrete spec is an error - sp.parse('{0} ~bvv'.format(specfile.strpath)) + sp.parse("{0} ~bvv".format(specfile.strpath)) # ======================================================================== # Lex checks @@ -707,7 +694,7 @@ class TestSpecSyntax(object): complex_lex, "mvapich_foo" "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug-qt_4" - "^stackwalker@8.1_1e" + "^stackwalker@8.1_1e", ) # The following lexes are non-ambiguous (add a space before -qt_4) @@ -717,99 +704,114 @@ class TestSpecSyntax(object): complex_lex, "mvapich_foo" "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug -qt_4" - "^stackwalker@8.1_1e") + "^stackwalker@8.1_1e", + ) self.check_lex( complex_lex, - "mvapich_foo" - "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug~qt_4" - "^stackwalker@8.1_1e") + "mvapich_foo" "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug~qt_4" "^stackwalker@8.1_1e", + ) def test_spaces_between_dependences(self): self.check_lex( complex_lex, "mvapich_foo " "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug -qt_4 " - "^stackwalker @ 8.1_1e") + "^stackwalker @ 8.1_1e", + ) self.check_lex( complex_lex, "mvapich_foo " "^_openmpi@1.2:1.4,1.6%intel@12.1:12.6+debug~qt_4 " - "^stackwalker @ 8.1_1e") + "^stackwalker @ 8.1_1e", + ) def test_spaces_between_options(self): self.check_lex( complex_lex, "mvapich_foo " "^_openmpi @1.2:1.4,1.6 %intel @12.1:12.6 +debug -qt_4 " - "^stackwalker @8.1_1e") + "^stackwalker @8.1_1e", + ) def test_way_too_many_spaces(self): self.check_lex( complex_lex, "mvapich_foo " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) self.check_lex( complex_lex, "mvapich_foo " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug ~ qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) def test_kv_with_quotes(self): self.check_lex( kv_lex, "mvapich_foo debug='4' " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) self.check_lex( kv_lex, 'mvapich_foo debug="4" ' "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) self.check_lex( kv_lex, "mvapich_foo 'debug = 4' " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) def test_kv_without_quotes(self): self.check_lex( kv_lex, "mvapich_foo debug=4 " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) def test_kv_with_spaces(self): self.check_lex( kv_lex, "mvapich_foo debug = 4 " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) self.check_lex( kv_lex, "mvapich_foo debug =4 " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") + "^ stackwalker @ 8.1_1e", + ) self.check_lex( kv_lex, "mvapich_foo debug= 4 " "^ _openmpi @1.2 : 1.4 , 1.6 % intel @ 12.1 : 12.6 + debug - qt_4 " - "^ stackwalker @ 8.1_1e") - - @pytest.mark.parametrize('expected_tokens,spec_string', [ - ([Token(sp.ID, 'target'), - Token(sp.EQ, '='), - Token(sp.VAL, 'broadwell')], - 'target=broadwell'), - ([Token(sp.ID, 'target'), - Token(sp.EQ, '='), - Token(sp.VAL, ':broadwell,icelake')], - 'target=:broadwell,icelake') - ]) + "^ stackwalker @ 8.1_1e", + ) + + @pytest.mark.parametrize( + "expected_tokens,spec_string", + [ + ( + [Token(sp.ID, "target"), Token(sp.EQ, "="), Token(sp.VAL, "broadwell")], + "target=broadwell", + ), + ( + [Token(sp.ID, "target"), Token(sp.EQ, "="), Token(sp.VAL, ":broadwell,icelake")], + "target=:broadwell,icelake", + ), + ], + ) def test_target_tokenization(self, expected_tokens, spec_string): self.check_lex(expected_tokens, spec_string) - @pytest.mark.regression('20310') + @pytest.mark.regression("20310") def test_compare_abstract_specs(self): """Spec comparisons must be valid for abstract specs. @@ -818,13 +820,13 @@ class TestSpecSyntax(object): # Add fields in order they appear in `Spec._cmp_node` constraints = [ None, - 'foo', - 'foo.foo', - 'foo.foo@foo', - 'foo.foo@foo+foo', - 'foo.foo@foo+foo arch=foo-foo-foo', - 'foo.foo@foo+foo arch=foo-foo-foo %foo', - 'foo.foo@foo+foo arch=foo-foo-foo %foo cflags=foo', + "foo", + "foo.foo", + "foo.foo@foo", + "foo.foo@foo+foo", + "foo.foo@foo+foo arch=foo-foo-foo", + "foo.foo@foo+foo arch=foo-foo-foo %foo", + "foo.foo@foo+foo arch=foo-foo-foo %foo cflags=foo", ] specs = [Spec(s) for s in constraints] |