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] spec = specs[0]
if not spack.repo.PATH.exists(spec.name): if not spack.repo.PATH.exists(spec.name):
tty.die( raise spack.repo.UnknownPackageError(spec.name)
"No package for '{0}' was found.".format(spec.name),
" Use `spack create` to create a new package",
)
if not spec.versions.concrete_range_as_version: if not spec.versions.concrete_range_as_version:
tty.die( tty.die(

View file

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

View file

@ -5,6 +5,7 @@
"""Service functions and classes to implement the hooks """Service functions and classes to implement the hooks
for Spack's command extensions. for Spack's command extensions.
""" """
import difflib
import importlib import importlib
import os import os
import re import re
@ -176,10 +177,19 @@ class CommandNotFoundError(spack.error.SpackError):
""" """
def __init__(self, cmd_name): def __init__(self, cmd_name):
super().__init__( msg = (
"{0} is not a recognized Spack command or extension command;" "{0} is not a recognized Spack command or extension command;"
" check with `spack commands`.".format(cmd_name) " 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): class ExtensionNamingError(spack.error.SpackError):

View file

@ -6,6 +6,7 @@
import abc import abc
import collections.abc import collections.abc
import contextlib import contextlib
import difflib
import errno import errno
import functools import functools
import importlib 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 = "Did you mean to specify a filename with './{0}'?"
long_msg = long_msg.format(name) long_msg = long_msg.format(name)
else: 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) super().__init__(msg, long_msg)
self.name = name 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): def test_dev_build_fails_nonexistent_package_name(mock_packages):
output = dev_build("no_such_package", fail_on_error=False) output = ""
assert "No package for 'no_such_package' was found" in 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): def test_dev_build_fails_no_version(mock_packages):