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:
parent
f6b23b4653
commit
141c7de5d8
5 changed files with 35 additions and 12 deletions
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue