summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authoralalazo <massimiliano.culpo@googlemail.com>2016-07-01 23:06:07 +0200
committeralalazo <massimiliano.culpo@googlemail.com>2016-07-01 23:06:07 +0200
commitd10fceaacc7a1f8349f249c8700381f858b359b1 (patch)
treee3b9e41f5e8587c36bd5938ff3cd53c59521a6a7 /lib
parent0e171127b99e06d05631fe6391cb20b813bc1747 (diff)
downloadspack-d10fceaacc7a1f8349f249c8700381f858b359b1.tar.gz
spack-d10fceaacc7a1f8349f249c8700381f858b359b1.tar.bz2
spack-d10fceaacc7a1f8349f249c8700381f858b359b1.tar.xz
spack-d10fceaacc7a1f8349f249c8700381f858b359b1.zip
spack commands : refactoring of cli arguments and common utiities. Implemented suggestions on `spack module loads`
- Common cli arguments now are in their own module - Moved utilities that can be reused by different commands into spack.cmd.__init__.py - Modifications to `spack module loads`
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/cmd/__init__.py101
-rw-r--r--lib/spack/spack/cmd/common/__init__.py24
-rw-r--r--lib/spack/spack/cmd/common/arguments.py88
-rw-r--r--lib/spack/spack/cmd/find.py85
-rw-r--r--lib/spack/spack/cmd/module.py140
-rw-r--r--lib/spack/spack/cmd/uninstall.py20
-rw-r--r--lib/spack/spack/test/cmd/find.py6
-rw-r--r--lib/spack/spack/util/pattern.py6
8 files changed, 271 insertions, 199 deletions
diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py
index 672999159c..02f6f5b98a 100644
--- a/lib/spack/spack/cmd/__init__.py
+++ b/lib/spack/spack/cmd/__init__.py
@@ -27,11 +27,12 @@ import re
import sys
import llnl.util.tty as tty
-from llnl.util.lang import attr_setdefault
-
import spack
-import spack.spec
import spack.config
+import spack.spec
+from llnl.util.lang import *
+from llnl.util.tty.colify import *
+from llnl.util.tty.color import *
#
# Settings for commands that modify configuration
@@ -145,3 +146,97 @@ def disambiguate_spec(spec):
tty.die(*args)
return matching_specs[0]
+
+
+def ask_for_confirmation(message):
+ while True:
+ tty.msg(message + '[y/n]')
+ choice = raw_input().lower()
+ if choice == 'y':
+ break
+ elif choice == 'n':
+ raise SystemExit('Operation aborted')
+ tty.warn('Please reply either "y" or "n"')
+
+
+def gray_hash(spec, length):
+ return colorize('@K{%s}' % spec.dag_hash(length))
+
+
+def display_specs(specs, **kwargs):
+ mode = kwargs.get('mode', 'short')
+ hashes = kwargs.get('long', False)
+ namespace = kwargs.get('namespace', False)
+ flags = kwargs.get('show_flags', False)
+ variants = kwargs.get('variants', False)
+
+ hlen = 7
+ if kwargs.get('very_long', False):
+ hashes = True
+ hlen = None
+
+ nfmt = '.' if namespace else '_'
+ ffmt = '$%+' if flags else ''
+ vfmt = '$+' if variants else ''
+ format_string = '$%s$@%s%s' % (nfmt, ffmt, vfmt)
+
+ # Make a dict with specs keyed by architecture and compiler.
+ index = index_by(specs, ('architecture', 'compiler'))
+
+ # Traverse the index and print out each package
+ for i, (architecture, compiler) in enumerate(sorted(index)):
+ if i > 0:
+ print
+
+ header = "%s{%s} / %s{%s}" % (spack.spec.architecture_color,
+ architecture, spack.spec.compiler_color,
+ compiler)
+ tty.hline(colorize(header), char='-')
+
+ specs = index[(architecture, compiler)]
+ specs.sort()
+
+ abbreviated = [s.format(format_string, color=True) for s in specs]
+ if mode == 'paths':
+ # Print one spec per line along with prefix path
+ width = max(len(s) for s in abbreviated)
+ width += 2
+ format = " %%-%ds%%s" % width
+
+ for abbrv, spec in zip(abbreviated, specs):
+ if hashes:
+ print(gray_hash(spec, hlen), )
+ print(format % (abbrv, spec.prefix))
+
+ elif mode == 'deps':
+ for spec in specs:
+ print(spec.tree(
+ format=format_string,
+ color=True,
+ indent=4,
+ prefix=(lambda s: gray_hash(s, hlen)) if hashes else None))
+
+ elif mode == 'short':
+ # Print columns of output if not printing flags
+ if not flags:
+
+ def fmt(s):
+ string = ""
+ if hashes:
+ string += gray_hash(s, hlen) + ' '
+ string += s.format('$-%s$@%s' % (nfmt, vfmt), color=True)
+
+ return string
+
+ colify(fmt(s) for s in specs)
+ # Print one entry per line if including flags
+ else:
+ for spec in specs:
+ # Print the hash if necessary
+ hsh = gray_hash(spec, hlen) + ' ' if hashes else ''
+ print(hsh + spec.format(format_string, color=True) + '\n')
+
+ else:
+ raise ValueError(
+ "Invalid mode for display_specs: %s. Must be one of (paths,"
+ "deps, short)." % mode) # NOQA: ignore=E501
diff --git a/lib/spack/spack/cmd/common/__init__.py b/lib/spack/spack/cmd/common/__init__.py
new file mode 100644
index 0000000000..ed1ec23bca
--- /dev/null
+++ b/lib/spack/spack/cmd/common/__init__.py
@@ -0,0 +1,24 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
diff --git a/lib/spack/spack/cmd/common/arguments.py b/lib/spack/spack/cmd/common/arguments.py
new file mode 100644
index 0000000000..a50bac5ac5
--- /dev/null
+++ b/lib/spack/spack/cmd/common/arguments.py
@@ -0,0 +1,88 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+
+import argparse
+
+import spack.modules
+from spack.util.pattern import Bunch
+__all__ = ['add_common_arguments']
+
+_arguments = {}
+
+
+def add_common_arguments(parser, list_of_arguments):
+ for argument in list_of_arguments:
+ if argument not in _arguments:
+ message = 'Trying to add the non existing argument "{0}" to a command'
+ raise KeyError(message.format(argument))
+ x = _arguments[argument]
+ parser.add_argument(*x.flags, **x.kwargs)
+
+
+class ConstraintAction(argparse.Action):
+ """Constructs a list of specs based on a constraint given on the command line
+
+ An instance of this class is supposed to be used as an argument action in a parser.
+
+ It will read a constraint and will attach a list of matching specs to the namespace
+ """
+ qualifiers = {}
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ # Query specs from command line
+ d = self.qualifiers.get(namespace.subparser_name, {})
+ specs = [s for s in spack.installed_db.query(**d)]
+ values = ' '.join(values)
+ if values:
+ specs = [x for x in specs if x.satisfies(values, strict=True)]
+ namespace.specs = specs
+
+_arguments['constraint'] = Bunch(flags=('constraint',),
+ kwargs={
+ 'nargs': '*',
+ 'help': 'Optional constraint to select a subset of installed packages',
+ 'action': ConstraintAction
+ })
+
+_arguments['module_type'] = Bunch(flags=('-m', '--module-type'),
+ kwargs={
+ 'help': 'Type of module files',
+ 'default': 'tcl',
+ 'choices': spack.modules.module_types
+ })
+
+_arguments['yes_to_all'] = Bunch(flags=('-y', '--yes-to-all'),
+ kwargs={
+ 'action': 'store_true',
+ 'dest': 'yes_to_all',
+ 'help': 'Assume "yes" is the answer to every confirmation asked to the user.'
+ })
+
+_arguments['recurse_dependencies'] = Bunch(flags=('-r', '--dependencies'),
+ kwargs={
+ 'action': 'store_true',
+ 'dest': 'recurse_dependencies',
+ 'help': 'Recursively traverse spec dependencies'
+ })
diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py
index 3ec671f93f..d3ea38c573 100644
--- a/lib/spack/spack/cmd/find.py
+++ b/lib/spack/spack/cmd/find.py
@@ -31,7 +31,7 @@ import spack.spec
from llnl.util.lang import *
from llnl.util.tty.colify import *
from llnl.util.tty.color import *
-from llnl.util.lang import *
+from spack.cmd import display_specs
description = "Find installed spack packages"
@@ -104,89 +104,6 @@ def setup_parser(subparser):
help='optional specs to filter results')
-def gray_hash(spec, length):
- return colorize('@K{%s}' % spec.dag_hash(length))
-
-
-def display_specs(specs, **kwargs):
- mode = kwargs.get('mode', 'short')
- hashes = kwargs.get('long', False)
- namespace = kwargs.get('namespace', False)
- flags = kwargs.get('show_flags', False)
- variants = kwargs.get('variants', False)
-
- hlen = 7
- if kwargs.get('very_long', False):
- hashes = True
- hlen = None
-
- nfmt = '.' if namespace else '_'
- ffmt = '$%+' if flags else ''
- vfmt = '$+' if variants else ''
- format_string = '$%s$@%s%s' % (nfmt, ffmt, vfmt)
-
- # Make a dict with specs keyed by architecture and compiler.
- index = index_by(specs, ('architecture', 'compiler'))
-
- # Traverse the index and print out each package
- for i, (architecture, compiler) in enumerate(sorted(index)):
- if i > 0:
- print
-
- header = "%s{%s} / %s{%s}" % (spack.spec.architecture_color,
- architecture, spack.spec.compiler_color,
- compiler)
- tty.hline(colorize(header), char='-')
-
- specs = index[(architecture, compiler)]
- specs.sort()
-
- abbreviated = [s.format(format_string, color=True) for s in specs]
- if mode == 'paths':
- # Print one spec per line along with prefix path
- width = max(len(s) for s in abbreviated)
- width += 2
- format = " %%-%ds%%s" % width
-
- for abbrv, spec in zip(abbreviated, specs):
- if hashes:
- print(gray_hash(spec, hlen), )
- print(format % (abbrv, spec.prefix))
-
- elif mode == 'deps':
- for spec in specs:
- print(spec.tree(
- format=format_string,
- color=True,
- indent=4,
- prefix=(lambda s: gray_hash(s, hlen)) if hashes else None))
-
- elif mode == 'short':
- # Print columns of output if not printing flags
- if not flags:
-
- def fmt(s):
- string = ""
- if hashes:
- string += gray_hash(s, hlen) + ' '
- string += s.format('$-%s$@%s' % (nfmt, vfmt), color=True)
-
- return string
-
- colify(fmt(s) for s in specs)
- # Print one entry per line if including flags
- else:
- for spec in specs:
- # Print the hash if necessary
- hsh = gray_hash(spec, hlen) + ' ' if hashes else ''
- print(hsh + spec.format(format_string, color=True) + '\n')
-
- else:
- raise ValueError(
- "Invalid mode for display_specs: %s. Must be one of (paths,"
- "deps, short)." % mode) # NOQA: ignore=E501
-
-
def query_arguments(args):
# Check arguments
if args.explicit and args.implicit:
diff --git a/lib/spack/spack/cmd/module.py b/lib/spack/spack/cmd/module.py
index 6f9ede21ac..2c47e9fcb6 100644
--- a/lib/spack/spack/cmd/module.py
+++ b/lib/spack/spack/cmd/module.py
@@ -24,102 +24,59 @@
##############################################################################
from __future__ import print_function
+import collections
import os
import shutil
import sys
-import collections
-import argparse
import llnl.util.tty as tty
import spack.cmd
+import spack.cmd.common.arguments as arguments
from llnl.util.filesystem import mkdirp
from spack.modules import module_types
-from spack.util.string import *
-
-from spack.cmd.uninstall import ask_for_confirmation
+#from spack.util.string import *
description = "Manipulate module files"
+callbacks = {}
-# Qualifiers to be used when querying the db for specs
-constraint_qualifiers = {
- 'refresh': {
- 'installed': True,
- 'known': True
- },
- 'find': {
- },
- 'load-list':{
- },
- 'rm': {
- }
-}
-
-
-class ConstraintAction(argparse.Action):
- qualifiers = {}
-
- def __call__(self, parser, namespace, values, option_string=None):
- # Query specs from command line
- d = self.qualifiers.get(namespace.subparser_name, {})
- specs = [s for s in spack.installed_db.query(**d)]
- values = ' '.join(values)
- if values:
- specs = [x for x in specs if x.satisfies(values, strict=True)]
- namespace.specs = specs
-
-# TODO : this needs better wrapping to be extracted
-ConstraintAction.qualifiers.update(constraint_qualifiers)
-
-def _add_common_arguments(subparser):
- type_help = 'Type of module files'
- subparser.add_argument('-m', '--module-type', help=type_help, default='tcl', choices=module_types)
- constraint_help = 'Optional constraint to select a subset of installed packages'
- subparser.add_argument('constraint', nargs='*', help=constraint_help, action=ConstraintAction)
+def subcommand(subparser_name):
+ """Registers a function in the callbacks dictionary"""
+ def decorator(callback):
+ callbacks[subparser_name] = callback
+ return callback
+ return decorator
def setup_parser(subparser):
sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name')
# spack module refresh
- refresh_parser = sp.add_parser('refresh', help='Regenerate all module files.')
+ refresh_parser = sp.add_parser('refresh', help='Regenerate module files')
refresh_parser.add_argument('--delete-tree', help='Delete the module file tree before refresh', action='store_true')
- _add_common_arguments(refresh_parser)
- refresh_parser.add_argument(
- '-y', '--yes-to-all', action='store_true', dest='yes_to_all',
- help='Assume "yes" is the answer to every confirmation asked to the user.'
- )
+ arguments.add_common_arguments(refresh_parser, ['constraint', 'module_type', 'yes_to_all'])
# spack module find
- find_parser = sp.add_parser('find', help='Find module files for packages.')
- _add_common_arguments(find_parser)
+ find_parser = sp.add_parser('find', help='Find module files for packages')
+ arguments.add_common_arguments(find_parser, ['constraint', 'module_type'])
# spack module rm
- rm_parser = sp.add_parser('rm', help='Find module files for packages.')
- _add_common_arguments(rm_parser)
- rm_parser.add_argument(
- '-y', '--yes-to-all', action='store_true', dest='yes_to_all',
- help='Assume "yes" is the answer to every confirmation asked to the user.'
- )
+ rm_parser = sp.add_parser('rm', help='Remove module files')
+ arguments.add_common_arguments(rm_parser, ['constraint', 'module_type', 'yes_to_all'])
- # spack module load-list
- loadlist_parser = sp.add_parser(
- 'load-list',
- help='Prompt the list of modules associated with packages that satisfy a contraint'
+ # spack module loads
+ loads_parser = sp.add_parser(
+ 'loads',
+ help='Prompt the list of modules associated with packages that satisfy a constraint'
)
- loadlist_parser.add_argument(
- '-d', '--dependencies', action='store_true',
- dest='recurse_dependencies',
- help='Recursively traverse spec dependencies')
-
- loadlist_parser.add_argument(
- '-s', '--shell', action='store_true', dest='shell',
- help='Generate shell script (instead of input for module command)')
+ loads_parser.add_argument(
+ '--input-only', action='store_false', dest='shell',
+ help='Generate input for module command (instead of a shell script)')
- loadlist_parser.add_argument(
+ loads_parser.add_argument(
'-p', '--prefix', dest='prefix', default='',
help='Prepend to module names when issuing module load commands')
- _add_common_arguments(loadlist_parser)
+ arguments.add_common_arguments(loads_parser, ['constraint', 'module_type', 'recurse_dependencies'])
class MultipleMatches(Exception):
@@ -129,8 +86,8 @@ class MultipleMatches(Exception):
class NoMatch(Exception):
pass
-
-def load_list(mtype, specs, args):
+@subcommand('loads')
+def loads(mtype, specs, args):
# Get a comprehensive list of specs
if args.recurse_dependencies:
specs_from_user_constraint = specs[:]
@@ -166,7 +123,7 @@ def load_list(mtype, specs, args):
d['name'] = mod
print(prompt_template.format(**d))
-
+@subcommand('find')
def find(mtype, specs, args):
"""
Look at all installed packages and see if the spec provided
@@ -187,9 +144,11 @@ def find(mtype, specs, args):
print(mod.use_name)
+@subcommand('rm')
def rm(mtype, specs, args):
module_cls = module_types[mtype]
- modules = [module_cls(spec) for spec in specs if os.path.exists(module_cls(spec).file_name)]
+ specs_with_modules = [spec for spec in specs if os.path.exists(module_cls(spec).file_name)]
+ modules = [module_cls(spec) for spec in specs_with_modules]
if not modules:
tty.msg('No module file matches your query')
@@ -197,21 +156,20 @@ def rm(mtype, specs, args):
# Ask for confirmation
if not args.yes_to_all:
- tty.msg('You are about to remove the following module files:\n')
- for s in modules:
- print(s.file_name)
+ tty.msg('You are about to remove {0} module files the following specs:\n'.format(mtype))
+ spack.cmd.display_specs(specs_with_modules, long=True)
print('')
- ask_for_confirmation('Do you want to proceed ? ')
+ spack.cmd.ask_for_confirmation('Do you want to proceed ? ')
# Remove the module files
for s in modules:
s.remove()
+@subcommand('refresh')
def refresh(mtype, specs, args):
- """
- Regenerate all module files for installed packages known to
- spack (some packages may no longer exist).
+ """Regenerate module files for installed packages
+
"""
# Prompt a message to the user about what is going to change
if not specs:
@@ -219,11 +177,10 @@ def refresh(mtype, specs, args):
return
if not args.yes_to_all:
- tty.msg('You are about to regenerate the {name} module files for the following specs:\n'.format(name=mtype))
- for s in specs:
- print(s.format(color=True))
+ tty.msg('You are about to regenerate {name} module files for the following specs:\n'.format(name=mtype))
+ spack.cmd.display_specs(specs, long=True)
print('')
- ask_for_confirmation('Do you want to proceed ? ')
+ spack.cmd.ask_for_confirmation('Do you want to proceed ? ')
cls = module_types[mtype]
@@ -252,16 +209,17 @@ def refresh(mtype, specs, args):
for x in writers:
x.write(overwrite=True)
-# Dictionary of callbacks based on the value of subparser_name
-callbacks = {
- 'refresh': refresh,
- 'find': find,
- 'load-list': load_list,
- 'rm': rm
-}
-
def module(parser, args):
+ # Qualifiers to be used when querying the db for specs
+ constraint_qualifiers = {
+ 'refresh': {
+ 'installed': True,
+ 'known': True
+ },
+ }
+ arguments.ConstraintAction.qualifiers.update(constraint_qualifiers)
+
module_type = args.module_type
constraint = args.constraint
try:
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py
index a6f08d09ed..92de33873b 100644
--- a/lib/spack/spack/cmd/uninstall.py
+++ b/lib/spack/spack/cmd/uninstall.py
@@ -30,7 +30,6 @@ import llnl.util.tty as tty
import spack
import spack.cmd
import spack.repository
-from spack.cmd.find import display_specs
description = "Remove an installed package"
@@ -47,17 +46,6 @@ display_args = {
}
-def ask_for_confirmation(message):
- while True:
- tty.msg(message + '[y/n]')
- choice = raw_input().lower()
- if choice == 'y':
- break
- elif choice == 'n':
- raise SystemExit('Operation aborted')
- tty.warn('Please reply either "y" or "n"')
-
-
def setup_parser(subparser):
subparser.add_argument(
'-f', '--force', action='store_true', dest='force',
@@ -99,7 +87,7 @@ def concretize_specs(specs, allow_multiple_matches=False, force=False):
if not allow_multiple_matches and len(matching) > 1:
tty.error("%s matches multiple packages:" % spec)
print()
- display_specs(matching, **display_args)
+ spack.cmd.display_specs(matching, **display_args)
print()
has_errors = True
@@ -179,7 +167,7 @@ def uninstall(parser, args):
tty.error("Will not uninstall %s" % spec.format("$_$@$%@$#", color=True))
print('')
print("The following packages depend on it:")
- display_specs(lst, **display_args)
+ spack.cmd.display_specs(lst, **display_args)
print('')
has_error = True
elif args.dependents:
@@ -193,9 +181,9 @@ def uninstall(parser, args):
if not args.yes_to_all:
tty.msg("The following packages will be uninstalled : ")
print('')
- display_specs(uninstall_list, **display_args)
+ spack.cmd.display_specs(uninstall_list, **display_args)
print('')
- ask_for_confirmation('Do you want to proceed ? ')
+ spack.cmd.ask_for_confirmation('Do you want to proceed ? ')
# Uninstall everything on the list
do_uninstall(uninstall_list, args.force)
diff --git a/lib/spack/spack/test/cmd/find.py b/lib/spack/spack/test/cmd/find.py
index 371e9650e0..fa82db7733 100644
--- a/lib/spack/spack/test/cmd/find.py
+++ b/lib/spack/spack/test/cmd/find.py
@@ -27,11 +27,7 @@
import spack.cmd.find
import unittest
-
-class Bunch(object):
-
- def __init__(self, **kwargs):
- self.__dict__.update(kwargs)
+from spack.util.pattern import Bunch
class FindTest(unittest.TestCase):
diff --git a/lib/spack/spack/util/pattern.py b/lib/spack/spack/util/pattern.py
index 6d4bcb1039..6af39c87d8 100644
--- a/lib/spack/spack/util/pattern.py
+++ b/lib/spack/spack/util/pattern.py
@@ -114,3 +114,9 @@ def composite(interface=None, method_list=None, container=list):
return wrapper_class
return cls_decorator
+
+
+class Bunch(object):
+ """Carries a bunch of named attributes (from Alex Martelli bunch)"""
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)