Skip to content
Snippets Groups Projects
Unverified Commit 04a6a55c authored by Todd Gamblin's avatar Todd Gamblin Committed by GitHub
Browse files

commands: add simple `spack commands --update-completion` argument (#14607)

Instead of another script, this adds a simple argument to `spack
commands` that updates the completion script.  Developers can now just
run:

    spack commands --update-completion

This should make it simpler for developers to remember to run this
*before* the tests fail.  Also, this version tab-completes.
parent 031fdfd7
No related branches found
No related tags found
No related merge requests found
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
import re import re
import sys import sys
import llnl.util.filesystem as fs
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.argparsewriter import ( from llnl.util.argparsewriter import (
ArgparseWriter, ArgparseRstWriter, ArgparseCompletionWriter ArgparseWriter, ArgparseRstWriter, ArgparseCompletionWriter
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
import spack.cmd import spack.cmd
import spack.main import spack.main
import spack.paths
from spack.main import section_descriptions from spack.main import section_descriptions
...@@ -31,6 +33,20 @@ ...@@ -31,6 +33,20 @@
formatters = {} formatters = {}
#: standard arguments for updating completion scripts
#: we iterate through these when called with --update-completion
update_completion_args = {
"bash": {
"aliases": True,
"format": "bash",
"header": os.path.join(
spack.paths.share_path, "bash", "spack-completion.in"),
"update": os.path.join(
spack.paths.share_path, "spack-completion.bash"),
},
}
def formatter(func): def formatter(func):
"""Decorator used to register formatters""" """Decorator used to register formatters"""
formatters[func.__name__] = func formatters[func.__name__] = func
...@@ -39,7 +55,12 @@ def formatter(func): ...@@ -39,7 +55,12 @@ def formatter(func):
def setup_parser(subparser): def setup_parser(subparser):
subparser.add_argument( subparser.add_argument(
'-a', '--aliases', action='store_true', help='include command aliases') "--update-completion", action='store_true', default=False,
help="regenerate spack's tab completion scripts")
subparser.add_argument(
'-a', '--aliases', action='store_true', default=False,
help='include command aliases')
subparser.add_argument( subparser.add_argument(
'--format', default='names', choices=formatters, '--format', default='names', choices=formatters,
help='format to be used to print the output (default: names)') help='format to be used to print the output (default: names)')
...@@ -229,7 +250,11 @@ def prepend_header(args, out): ...@@ -229,7 +250,11 @@ def prepend_header(args, out):
out.write(header.read()) out.write(header.read())
def commands(parser, args): def _commands(parser, args):
"""This is the 'regular' command, which can be called multiple times.
See ``commands()`` below for ``--update-completion`` handling.
"""
formatter = formatters[args.format] formatter = formatters[args.format]
# check header first so we don't open out files unnecessarily # check header first so we don't open out files unnecessarily
...@@ -255,6 +280,37 @@ def commands(parser, args): ...@@ -255,6 +280,37 @@ def commands(parser, args):
prepend_header(args, f) prepend_header(args, f)
formatter(args, f) formatter(args, f)
if args.update_completion:
fs.set_executable(args.update)
else: else:
prepend_header(args, sys.stdout) prepend_header(args, sys.stdout)
formatter(args, sys.stdout) formatter(args, sys.stdout)
def update_completion(parser, args):
"""Iterate through the shells and update the standard completion files.
This is a convenience method to avoid calling this command many
times, and to simplify completion update for developers.
"""
for shell, shell_args in update_completion_args.items():
for attr, value in shell_args.items():
setattr(args, attr, value)
_commands(parser, args)
def commands(parser, args):
if args.update_completion:
if args.format != 'names' or any([
args.aliases, args.update, args.header
]):
tty.die("--update-completion can only be specified alone.")
# this runs the command multiple times with different arguments
return update_completion(parser, args)
else:
# run commands normally
return _commands(parser, args)
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import filecmp import filecmp
import os import os
import shutil
import subprocess import subprocess
import pytest import pytest
...@@ -208,12 +209,47 @@ def test_bash_completion(): ...@@ -208,12 +209,47 @@ def test_bash_completion():
assert '_spack_compiler_add() {' in out2 assert '_spack_compiler_add() {' in out2
def test_update_completion_arg(tmpdir, monkeypatch):
mock_infile = tmpdir.join("spack-completion.in")
mock_bashfile = tmpdir.join("spack-completion.bash")
mock_args = {
"bash": {
"aliases": True,
"format": "bash",
"header": str(mock_infile),
"update": str(mock_bashfile),
},
}
# make a mock completion file missing the --update-completion argument
real_args = spack.cmd.commands.update_completion_args
shutil.copy(real_args['bash']['header'], mock_args['bash']['header'])
with open(real_args['bash']['update']) as old:
old_file = old.read()
with open(mock_args['bash']['update'], 'w') as mock:
mock.write(old_file.replace("--update-completion", ""))
mock_bashfile.setmtime(0) # ensure mtime triggers update
monkeypatch.setattr(
spack.cmd.commands, 'update_completion_args', mock_args)
# ensure things fail if --update-completion isn't specified alone
with pytest.raises(spack.main.SpackCommandError):
commands("--update-completion", "-a")
# ensure arg is restored
assert "--update-completion" not in mock_bashfile.read()
commands("--update-completion")
assert "--update-completion" in mock_bashfile.read()
def test_updated_completion_scripts(tmpdir): def test_updated_completion_scripts(tmpdir):
"""Make sure our shell tab completion scripts remain up-to-date.""" """Make sure our shell tab completion scripts remain up-to-date."""
msg = ("It looks like Spack's command-line interface has been modified. " msg = ("It looks like Spack's command-line interface has been modified. "
"Please update Spack's shell tab completion scripts by running:\n\n" "Please update Spack's shell tab completion scripts by running:\n\n"
" share/spack/qa/update-completion-scripts.sh\n\n" " spack commands --update-completion\n\n"
"and adding the changed files to your pull request.") "and adding the changed files to your pull request.")
for shell in ['bash']: # 'zsh', 'fish']: for shell in ['bash']: # 'zsh', 'fish']:
......
#!/usr/bin/env bash
#
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
# Updates Spack's shell tab completion scripts
# Switch to parent directory
QA_DIR="$(dirname "${BASH_SOURCE[0]}")"
cd "$QA_DIR/.."
# Update each shell
for shell in bash # zsh fish
do
header=$shell/spack-completion.in
script=spack-completion.$shell
rm -f $script
spack commands --aliases --format=$shell --header=$header --update=$script
chmod +x $script
done
...@@ -507,7 +507,7 @@ _spack_clone() { ...@@ -507,7 +507,7 @@ _spack_clone() {
_spack_commands() { _spack_commands() {
if $list_options if $list_options
then then
SPACK_COMPREPLY="-h --help -a --aliases --format --header --update" SPACK_COMPREPLY="-h --help --update-completion -a --aliases --format --header --update"
else else
SPACK_COMPREPLY="" SPACK_COMPREPLY=""
fi fi
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment