diff options
author | Todd Gamblin <gamblin2@llnl.gov> | 2022-05-23 23:33:43 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-24 03:33:43 +0000 |
commit | 306bed48d747c907cfbd60073cdf6d92c3bca14a (patch) | |
tree | 3e06e6dd1eb4c8b34a8ddbf47128683784e8d3e9 | |
parent | 63402c512b4d660e987dae6ad82f6ab7eac8a766 (diff) | |
download | spack-306bed48d747c907cfbd60073cdf6d92c3bca14a.tar.gz spack-306bed48d747c907cfbd60073cdf6d92c3bca14a.tar.bz2 spack-306bed48d747c907cfbd60073cdf6d92c3bca14a.tar.xz spack-306bed48d747c907cfbd60073cdf6d92c3bca14a.zip |
specs: emit better parsing errors for specs. (#24860)
Parse error information is kept for specs, but it doesn't seem like we propagate it
to the user when we encounter an error. This fixes that.
e.g., for this error in a package:
```python
depends_on("python@:3.8", when="0.900:")
```
Before, with no context and no clue that it's even from a particular spec:
```
==> Error: Unexpected token: ':'
```
With this PR:
```
==> Error: Unexpected token: ':'
Encountered when parsing spec:
0.900:
^
```
-rw-r--r-- | lib/spack/spack/cmd/__init__.py | 34 | ||||
-rw-r--r-- | lib/spack/spack/parse.py | 4 | ||||
-rw-r--r-- | lib/spack/spack/spec.py | 10 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/spec.py | 11 | ||||
-rw-r--r-- | lib/spack/spack/test/spec_syntax.py | 1 |
5 files changed, 33 insertions, 27 deletions
diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py index 9568ccc296..ff6c3d7bf4 100644 --- a/lib/spack/spack/cmd/__init__.py +++ b/lib/spack/spack/cmd/__init__.py @@ -155,31 +155,17 @@ def parse_specs(args, **kwargs): normalize = kwargs.get('normalize', False) tests = kwargs.get('tests', False) - try: - sargs = args - if not isinstance(args, six.string_types): - sargs = ' '.join(spack.util.string.quote(args)) - specs = spack.spec.parse(sargs) - for spec in specs: - if concretize: - spec.concretize(tests=tests) # implies normalize - elif normalize: - spec.normalize(tests=tests) - - return specs - - except spack.spec.SpecParseError as e: - msg = e.message + "\n" + str(e.string) + "\n" - msg += (e.pos + 2) * " " + "^" - raise spack.error.SpackError(msg) - - except spack.error.SpecError as e: - - msg = e.message - if e.long_message: - msg += e.long_message + sargs = args + if not isinstance(args, six.string_types): + sargs = ' '.join(spack.util.string.quote(args)) + specs = spack.spec.parse(sargs) + for spec in specs: + if concretize: + spec.concretize(tests=tests) # implies normalize + elif normalize: + spec.normalize(tests=tests) - raise spack.error.SpackError(msg) + return specs def matching_spec_from_env(spec): diff --git a/lib/spack/spack/parse.py b/lib/spack/spack/parse.py index 603848a3a0..e06338e0c8 100644 --- a/lib/spack/spack/parse.py +++ b/lib/spack/spack/parse.py @@ -123,11 +123,11 @@ class Parser(object): def next_token_error(self, message): """Raise an error about the next token in the stream.""" - raise ParseError(message, self.text, self.token.end) + raise ParseError(message, self.text[0], self.token.end) def last_token_error(self, message): """Raise an error about the previous token in the stream.""" - raise ParseError(message, self.text, self.token.start) + raise ParseError(message, self.text[0], self.token.start) def unexpected_token(self): self.next_token_error("Unexpected token: '%s'" % self.next.value) diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index db6eafaf37..3046aa1d08 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -5386,6 +5386,16 @@ class SpecParseError(spack.error.SpecError): self.string = parse_error.string self.pos = parse_error.pos + @property + def long_message(self): + return "\n".join( + [ + " Encountered when parsing spec:", + " %s" % self.string, + " %s^" % (" " * self.pos), + ] + ) + class DuplicateDependencyError(spack.error.SpecError): """Raised when the same dependency occurs in a spec twice.""" diff --git a/lib/spack/spack/test/cmd/spec.py b/lib/spack/spack/test/cmd/spec.py index 3fb25a3ea8..42f4c7a59a 100644 --- a/lib/spack/spack/test/cmd/spec.py +++ b/lib/spack/spack/test/cmd/spec.py @@ -124,6 +124,17 @@ def test_spec_returncode(): assert spec.returncode == 1 +def test_spec_parse_error(): + with pytest.raises(spack.spec.SpecParseError) as e: + spec("1.15:") + + # make sure the error is formatted properly + error_msg = """\ + 1.15: + ^""" + assert error_msg in e.value.long_message + + def test_env_aware_spec(mutable_mock_env_path): env = ev.create('test') env.add('mpileaks') diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py index 3971e02d48..815a46847a 100644 --- a/lib/spack/spack/test/spec_syntax.py +++ b/lib/spack/spack/test/spec_syntax.py @@ -125,7 +125,6 @@ class TestSpecSyntax(object): def _check_raises(self, exc_type, items): for item in items: with pytest.raises(exc_type): - print("CHECKING: ", item, "=======================") Spec(item) # ======================================================================== |