Skip to content

Commit

Permalink
CLI: do not provide command hints during tab-completion (#5012)
Browse files Browse the repository at this point in the history
The `verdi` command uses a custom `Group` subclass that overrides the
`get_command` method to provide some additional information in case the
provided command name does not exist. These hints should help the user
spot potential typos by giving a list of existing commands with similar
names.

However, this feature was also being triggered during tab-completion.
For example, typing `verdi comput list` followed by activating
tab-completion would result in the error being displayed since `comput`
is not a valid command. In this case, one does not want to display the
error message with hint at all.

The tricky part is that there is no canonical way to distinguish between
a normal command execution and a tab-completion event. The best bet is
to use the `resilient_parsing` attribute on the `Context` which is set
to `True` during tab-completion. Although this attribute was introduced
into `click` directly to support auto-completion functionality, the
problem is that this is not the only use case for which this flag can be
set. It is therefore possible that there is some code path where this
flag is set to `True` but it does not actually correspond to a
tab-completion event. For now there doesn't seem to be a better solution
though and in most cases this approach should work.

Cherry-pick: 8e763bb
  • Loading branch information
sphuber committed Aug 9, 2021
1 parent 80f0d7c commit 3df48bd
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions aiida/cmdline/commands/cmd_verdi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
# For further information please visit http://www.aiida.net #
###########################################################################
"""The main `verdi` click group."""

import difflib

import click

from aiida import __version__
Expand Down Expand Up @@ -52,23 +52,33 @@ def get_command(self, ctx, cmd_name):
"""
cmd = click.Group.get_command(self, ctx, cmd_name)

# return the exact match
# If we match an actual command, simply return the match
if cmd is not None:
return cmd

# If this command is called during tab-completion, we do not want to print an error message if the command can't
# be found, but instead we want to simply return here. However, in a normal command execution, we do want to
# execute the rest of this method to try and match commands that are similar in order to provide the user with
# some hints. The problem is that there is no one canonical way to determine whether the invocation is due to a
# normal command execution or a tab-complete operation. The `resilient_parsing` attribute of the `Context` is
# designed to allow things like tab-completion, however, it is not the only purpose. For now this is our best
# bet though to detect a tab-complete event. When `resilient_parsing` is switched on, we assume a tab-complete
# and do nothing in case the command name does not match an actual command.
if ctx.resilient_parsing:
return

if int(cmd_name.lower().encode('utf-8').hex(), 16) == 0x6769757365707065:
import base64
import gzip
click.echo(gzip.decompress(base64.b85decode(GIU.encode('utf-8'))).decode('utf-8'))
return None

# we might get better results with the Levenshtein distance
# or more advanced methods implemented in FuzzyWuzzy or similar libs,
# but this is an easy win for now
# We might get better results with the Levenshtein distance or more advanced methods implemented in FuzzyWuzzy
# or similar libs, but this is an easy win for now.
matches = difflib.get_close_matches(cmd_name, self.list_commands(ctx), cutoff=0.5)

if not matches:
# single letters are sometimes not matched, try with a simple startswith
# Single letters are sometimes not matched so also try with a simple startswith
matches = [c for c in sorted(self.list_commands(ctx)) if c.startswith(cmd_name)][:3]

if matches:
Expand Down

0 comments on commit 3df48bd

Please sign in to comment.