Add command and package suggestions (#40895)

* Add command suggestions

This adds suggestions of similar commands in case users mistype a
command. Before:
```
$ spack spack
==> Error: spack is not a recognized Spack command or extension command; check with `spack commands`.
```
After:
```
$ spack spack
==> Error: spack is not a recognized Spack command or extension command; check with `spack commands`.

Did you mean one of the following commands?
  spec
  patch
```

* Add package name suggestions

* Remove suggestion to run spack clean -m
This commit is contained in:
Michael Kuhn 2023-11-05 23:32:09 +01:00 committed by GitHub
parent f6b23b4653
commit 141c7de5d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 12 deletions

View file

@ -99,10 +99,7 @@ def dev_build(self, args):
spec = specs[0]
if not spack.repo.PATH.exists(spec.name):
tty.die(
"No package for '{0}' was found.".format(spec.name),
" Use `spack create` to create a new package",
)
raise spack.repo.UnknownPackageError(spec.name)
if not spec.versions.concrete_range_as_version:
tty.die(

View file

@ -43,10 +43,7 @@ def edit_package(name, repo_path, namespace):
if not os.access(path, os.R_OK):
tty.die("Insufficient permissions on '%s'!" % path)
else:
tty.die(
"No package for '{0}' was found.".format(spec.name),
" Use `spack create` to create a new package",
)
raise spack.repo.UnknownPackageError(spec.name)
editor(path)

View file

@ -5,6 +5,7 @@
"""Service functions and classes to implement the hooks
for Spack's command extensions.
"""
import difflib
import importlib
import os
import re
@ -176,10 +177,19 @@ class CommandNotFoundError(spack.error.SpackError):
"""
def __init__(self, cmd_name):
super().__init__(
msg = (
"{0} is not a recognized Spack command or extension command;"
" check with `spack commands`.".format(cmd_name)
)
long_msg = None
similar = difflib.get_close_matches(cmd_name, spack.cmd.all_commands())
if 1 <= len(similar) <= 5:
long_msg = "\nDid you mean one of the following commands?\n "
long_msg += "\n ".join(similar)
super().__init__(msg, long_msg)
class ExtensionNamingError(spack.error.SpackError):

View file

@ -6,6 +6,7 @@
import abc
import collections.abc
import contextlib
import difflib
import errno
import functools
import importlib
@ -1516,7 +1517,18 @@ def __init__(self, name, repo=None):
long_msg = "Did you mean to specify a filename with './{0}'?"
long_msg = long_msg.format(name)
else:
long_msg = "You may need to run 'spack clean -m'."
long_msg = "Use 'spack create' to create a new package."
if not repo:
repo = spack.repo.PATH
# We need to compare the base package name
pkg_name = name.rsplit(".", 1)[-1]
similar = difflib.get_close_matches(pkg_name, repo.all_package_names())
if 1 <= len(similar) <= 5:
long_msg += "\n\nDid you mean one of the following packages?\n "
long_msg += "\n ".join(similar)
super().__init__(msg, long_msg)
self.name = name

View file

@ -163,8 +163,15 @@ def test_dev_build_fails_multiple_specs(mock_packages):
def test_dev_build_fails_nonexistent_package_name(mock_packages):
output = dev_build("no_such_package", fail_on_error=False)
assert "No package for 'no_such_package' was found" in output
output = ""
try:
dev_build("no_such_package")
assert False, "no exception was raised!"
except spack.repo.UnknownPackageError as e:
output = e.message
assert "Package 'no_such_package' not found" in output
def test_dev_build_fails_no_version(mock_packages):