From 287b04e50a8a132f224def6ee1b36e8f4d0b105f Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Sun, 23 Nov 2014 17:53:21 -0600 Subject: Bugfix in terminal_size() --- lib/spack/llnl/util/tty/__init__.py | 23 +++++++++++++---------- lib/spack/llnl/util/tty/colify.py | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/__init__.py b/lib/spack/llnl/util/tty/__init__.py index 652d1cc1a8..5eeab67d6f 100644 --- a/lib/spack/llnl/util/tty/__init__.py +++ b/lib/spack/llnl/util/tty/__init__.py @@ -25,6 +25,9 @@ import sys import os import textwrap +import fcntl +import termios +import struct from StringIO import StringIO from llnl.util.tty.color import * @@ -155,7 +158,7 @@ def hline(label=None, **kwargs): color = kwargs.get('color', '') max_width = kwargs.get('max_width', 64) - cols, rows = terminal_size() + rows, cols = terminal_size() if not cols: cols = max_width else: @@ -178,22 +181,22 @@ def hline(label=None, **kwargs): def terminal_size(): - """Gets the dimensions of the console: cols, rows.""" + """Gets the dimensions of the console: (rows, cols).""" def ioctl_GWINSZ(fd): try: - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + rc = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) except: return - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: + return rc + rc = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not rc: try: fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) + rc = ioctl_GWINSZ(fd) os.close(fd) except: pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + if not rc: + rc = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - return int(cr[1]), int(cr[0]) + return int(rc[0]), int(rc[1]) diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py index ff06241937..0d068351a6 100644 --- a/lib/spack/llnl/util/tty/colify.py +++ b/lib/spack/llnl/util/tty/colify.py @@ -121,7 +121,7 @@ def colify(elts, **options): console_cols = options.get("cols", None) if not console_cols: - console_cols, console_rows = terminal_size() + console_rows, console_cols = terminal_size() elif type(console_cols) != int: raise ValueError("Number of columns must be an int") console_cols = max(1, console_cols - indent) @@ -167,7 +167,7 @@ def colified(elts, **options): if __name__ == "__main__": import optparse - cols, rows = terminal_size() + rows, cols = terminal_size() parser = optparse.OptionParser() parser.add_option("-u", "--uniform", action="store_true", default=False, help="Use uniformly sized columns instead of variable-size.") -- cgit v1.2.3-70-g09d2 From 22e4d11010059614bfc310a4d459be2f84524c87 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Sun, 23 Nov 2014 17:55:57 -0600 Subject: Cleanup code in colify. --- lib/spack/llnl/util/tty/colify.py | 109 ++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py index 0d068351a6..300bcfdd79 100644 --- a/lib/spack/llnl/util/tty/colify.py +++ b/lib/spack/llnl/util/tty/colify.py @@ -22,16 +22,9 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -# colify -# By Todd Gamblin, tgamblin@llnl.gov -# -# Takes a list of items as input and finds a good columnization of them, -# similar to how gnu ls does. You can pipe output to this script and -# get a tight display for it. This supports both uniform-width and -# variable-width (tighter) columns. -# -# Run colify -h for more information. -# +""" +Routines for printing columnar output. See colify() for more information. +""" import os import sys import fcntl @@ -54,12 +47,21 @@ class ColumnConfig: return "" % ", ".join("%s: %r" % a for a in attrs) -def config_variable_cols(elts, console_cols, padding): +def config_variable_cols(elts, console_width, padding): + """Variable-width column fitting algorithm. + + This function determines the most columns that can fit in the + screen width. Unlike uniform fitting, where all columns take + the width of the longest element in the list, each column takes + the width of its own longest element. This packs elements more + efficiently on screen. + """ # Get a bound on the most columns we could possibly have. lengths = [len(elt) for elt in elts] - max_cols = max(1, console_cols / (min(lengths) + padding)) + max_cols = max(1, console_width / (min(lengths) + padding)) max_cols = min(len(elts), max_cols) + # Determine the most columns possible for the console width. configs = [ColumnConfig(c) for c in xrange(1, max_cols+1)] for elt, length in enumerate(lengths): for i, conf in enumerate(configs): @@ -72,7 +74,7 @@ def config_variable_cols(elts, console_cols, padding): if conf.widths[col] < padded: conf.line_length += padded - conf.widths[col] conf.widths[col] = padded - conf.valid = (conf.line_length < console_cols) + conf.valid = (conf.line_length < console_width) try: config = next(conf for conf in reversed(configs) if conf.valid) @@ -85,26 +87,55 @@ def config_variable_cols(elts, console_cols, padding): return config -def config_uniform_cols(elts, console_cols, padding): +def config_uniform_cols(elts, console_width, padding): + """Uniform-width column fitting algorithm. + + Determines the longest element in the list, and determines how + many columns of that width will fit on screen. Returns a + corresponding column config. + """ max_len = max(len(elt) for elt in elts) + padding - cols = max(1, console_cols / max_len) + cols = max(1, console_width / max_len) cols = min(len(elts), cols) config = ColumnConfig(cols) config.widths = [max_len] * cols return config -def isatty(ostream): - force = os.environ.get('COLIFY_TTY', 'false').lower() != 'false' - return force or ostream.isatty() +def colify(elts, **options): + """Takes a list of elements as input and finds a good columnization + of them, similar to how gnu ls does. This supports both + uniform-width and variable-width (tighter) columns. + + If elts is not a list of strings, each element is first conveted + using str(). + Keyword arguments: -def colify(elts, **options): + output= A file object to write to. Default is sys.stdout. + indent= Optionally indent all columns by some number of spaces. + padding= Spaces between columns. Default is 2. + + tty= Whether to attempt to write to a tty. Default is to + autodetect a tty. Set to False to force single-column output. + + method= Method to use to fit columns. Options are variable or uniform. + Variable-width columns are tighter, uniform columns are all the + same width and fit less data on the screen. + + width= Width of the output. Default is 80 if tty is not detected. + """ # Get keyword arguments or set defaults - output = options.get("output", sys.stdout) - indent = options.get("indent", 0) - padding = options.get("padding", 2) - tty = options.get('tty', None) + output = options.pop("output", sys.stdout) + indent = options.pop("indent", 0) + padding = options.pop("padding", 2) + tty = options.pop('tty', None) + method = options.pop("method", "variable") + console_cols = options.pop("width", None) + + if options: + raise TypeError("'%s' is an invalid keyword argument for this function." + % next(options.iterkeys())) # elts needs to be an array of strings so we can count the elements elts = [str(elt) for elt in elts] @@ -112,21 +143,21 @@ def colify(elts, **options): return (0, ()) if not tty: - if tty is False or not isatty(output): + if tty is False or not output.isatty(): for elt in elts: output.write("%s\n" % elt) maxlen = max(len(str(s)) for s in elts) return (1, (maxlen,)) - console_cols = options.get("cols", None) + # Specify the number of character columns to use. if not console_cols: console_rows, console_cols = terminal_size() elif type(console_cols) != int: raise ValueError("Number of columns must be an int") console_cols = max(1, console_cols - indent) - method = options.get("method", "variable") + # Choose a method. Variable-width colums vs uniform-width. if method == "variable": config = config_variable_cols(elts, console_cols, padding) elif method == "uniform": @@ -162,29 +193,3 @@ def colified(elts, **options): options['output'] = sio colify(elts, **options) return sio.getvalue() - - -if __name__ == "__main__": - import optparse - - rows, cols = terminal_size() - parser = optparse.OptionParser() - parser.add_option("-u", "--uniform", action="store_true", default=False, - help="Use uniformly sized columns instead of variable-size.") - parser.add_option("-p", "--padding", metavar="PADDING", action="store", - type=int, default=2, help="Spaces to add between columns. Default is 2.") - parser.add_option("-i", "--indent", metavar="SPACES", action="store", - type=int, default=0, help="Indent the output by SPACES. Default is 0.") - parser.add_option("-w", "--width", metavar="COLS", action="store", - type=int, default=cols, help="Indent the output by SPACES. Default is 0.") - options, args = parser.parse_args() - - method = "variable" - if options.uniform: - method = "uniform" - - if sys.stdin.isatty(): - parser.print_help() - sys.exit(1) - else: - colify([line.strip() for line in sys.stdin], method=method, **options.__dict__) -- cgit v1.2.3-70-g09d2 From 72c753b93e368441ba7fd281c74886c5e52a820c Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 1 Dec 2014 21:29:01 -0800 Subject: Colify now supports fixing the number of columns. --- lib/spack/llnl/util/tty/colify.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py index 300bcfdd79..2c93eaadd5 100644 --- a/lib/spack/llnl/util/tty/colify.py +++ b/lib/spack/llnl/util/tty/colify.py @@ -47,7 +47,7 @@ class ColumnConfig: return "" % ", ".join("%s: %r" % a for a in attrs) -def config_variable_cols(elts, console_width, padding): +def config_variable_cols(elts, console_width, padding, cols=0): """Variable-width column fitting algorithm. This function determines the most columns that can fit in the @@ -55,20 +55,28 @@ def config_variable_cols(elts, console_width, padding): the width of the longest element in the list, each column takes the width of its own longest element. This packs elements more efficiently on screen. + + If cols is nonzero, force """ + if cols < 0: + raise ValueError("cols must be non-negative.") + # Get a bound on the most columns we could possibly have. lengths = [len(elt) for elt in elts] max_cols = max(1, console_width / (min(lengths) + padding)) max_cols = min(len(elts), max_cols) + # Range of column counts to try. If forced, use the supplied value. + col_range = [cols] if cols else xrange(1, max_cols+1) + # Determine the most columns possible for the console width. - configs = [ColumnConfig(c) for c in xrange(1, max_cols+1)] + configs = [ColumnConfig(c) for c in col_range] for elt, length in enumerate(lengths): - for i, conf in enumerate(configs): + for conf in configs: if conf.valid: - col = elt / ((len(elts) + i) / (i + 1)) + col = elt / ((len(elts) + conf.cols - 1) / conf.cols) padded = length - if col < i: + if col < (conf.cols - 1): padded += padding if conf.widths[col] < padded: @@ -87,16 +95,20 @@ def config_variable_cols(elts, console_width, padding): return config -def config_uniform_cols(elts, console_width, padding): +def config_uniform_cols(elts, console_width, padding, cols=0): """Uniform-width column fitting algorithm. Determines the longest element in the list, and determines how many columns of that width will fit on screen. Returns a corresponding column config. """ + if cols < 0: + raise ValueError("cols must be non-negative.") + max_len = max(len(elt) for elt in elts) + padding - cols = max(1, console_width / max_len) - cols = min(len(elts), cols) + if cols == 0: + cols = max(1, console_width / max_len) + cols = min(len(elts), cols) config = ColumnConfig(cols) config.widths = [max_len] * cols return config @@ -115,6 +127,10 @@ def colify(elts, **options): output= A file object to write to. Default is sys.stdout. indent= Optionally indent all columns by some number of spaces. padding= Spaces between columns. Default is 2. + width= Width of the output. Default is 80 if tty is not detected. + + cols= Force number of columns. Default is to size to terminal, + or single-column if no tty tty= Whether to attempt to write to a tty. Default is to autodetect a tty. Set to False to force single-column output. @@ -123,15 +139,19 @@ def colify(elts, **options): Variable-width columns are tighter, uniform columns are all the same width and fit less data on the screen. - width= Width of the output. Default is 80 if tty is not detected. + decorator= Function to add decoration (such as color) after columns have + already been fitted. Useful for fitting based only on + positive-width characters. """ # Get keyword arguments or set defaults + cols = options.pop("cols", 0) output = options.pop("output", sys.stdout) indent = options.pop("indent", 0) padding = options.pop("padding", 2) tty = options.pop('tty', None) method = options.pop("method", "variable") console_cols = options.pop("width", None) + decorator = options.pop("decorator", lambda x:x) if options: raise TypeError("'%s' is an invalid keyword argument for this function." @@ -159,9 +179,9 @@ def colify(elts, **options): # Choose a method. Variable-width colums vs uniform-width. if method == "variable": - config = config_variable_cols(elts, console_cols, padding) + config = config_variable_cols(elts, console_cols, padding, cols) elif method == "uniform": - config = config_uniform_cols(elts, console_cols, padding) + config = config_uniform_cols(elts, console_cols, padding, cols) else: raise ValueError("method must be one of: " + allowed_methods) @@ -176,7 +196,7 @@ def colify(elts, **options): output.write(" " * indent) for col in xrange(cols): elt = col * rows + row - output.write(formats[col] % elts[elt]) + output.write(formats[col] % decorator(elts[elt])) output.write("\n") row += 1 -- cgit v1.2.3-70-g09d2 From e15316e8256175359ef38066900df45b8b4d126c Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 1 Dec 2014 23:13:09 -0800 Subject: index_by supports compound index keys. --- lib/spack/llnl/util/lang.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py index ce7d0197f0..049d158c6d 100644 --- a/lib/spack/llnl/util/lang.py +++ b/lib/spack/llnl/util/lang.py @@ -68,6 +68,12 @@ def index_by(objects, *funcs): index1 = index_by(list_of_specs, 'arch', 'compiler') index2 = index_by(list_of_specs, 'compiler') + + You can also index by tuples by passing tuples: + + index1 = index_by(list_of_specs, ('arch', 'compiler')) + + Keys in the resulting dict will look like ('gcc', 'bgqos_0'). """ if not funcs: return objects @@ -75,6 +81,8 @@ def index_by(objects, *funcs): f = funcs[0] if isinstance(f, basestring): f = lambda x: getattr(x, funcs[0]) + elif isinstance(f, tuple): + f = lambda x: tuple(getattr(x, p) for p in funcs[0]) result = {} for o in objects: -- cgit v1.2.3-70-g09d2 From 40b4fa544362d2942ef7663c7fc809c1f5ef591b Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 1 Dec 2014 23:14:06 -0800 Subject: Better spack find view. --- lib/spack/llnl/util/tty/colify.py | 21 +++++++++++- lib/spack/spack/cmd/find.py | 70 ++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 35 deletions(-) (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py index 2c93eaadd5..5c5c6077ec 100644 --- a/lib/spack/llnl/util/tty/colify.py +++ b/lib/spack/llnl/util/tty/colify.py @@ -196,7 +196,7 @@ def colify(elts, **options): output.write(" " * indent) for col in xrange(cols): elt = col * rows + row - output.write(formats[col] % decorator(elts[elt])) + output.write(decorator(formats[col] % elts[elt])) output.write("\n") row += 1 @@ -206,6 +206,25 @@ def colify(elts, **options): return (config.cols, tuple(config.widths)) +def colify_table(table, **options): + if table is None: + raise TypeError("Can't call colify_table on NoneType") + elif not table or not table[0]: + raise ValueError("Table is empty in colify_table!") + + columns = len(table[0]) + def transpose(): + for i in xrange(columns): + for row in table: + yield row[i] + + if 'cols' in options: + raise ValueError("Cannot override columsn in colify_table.") + options['cols'] = columns + + colify(transpose(), **options) + + def colified(elts, **options): """Invokes the colify() function but returns the result as a string instead of writing it to an output string.""" diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index 2238484a21..c0a3162429 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -24,13 +24,14 @@ ############################################################################## import sys import collections +import itertools from external import argparse from StringIO import StringIO import llnl.util.tty as tty -from llnl.util.tty.colify import colify +from llnl.util.tty.colify import * from llnl.util.tty.color import * -from llnl.util.lang import partition_list, index_by +from llnl.util.lang import * import spack import spack.spec @@ -65,39 +66,40 @@ def find(parser, args): if not query_specs: return - specs = [s for s in spack.db.installed_package_specs() - if not query_specs or any(s.satisfies(q) for q in query_specs)] + # Get all the specs the user asked for + if not query_specs: + specs = set(spack.db.installed_package_specs()) + else: + results = [set(spack.db.get_installed(qs)) for qs in query_specs] + specs = set.union(*results) # Make a dict with specs keyed by architecture and compiler. - index = index_by(specs, 'architecture', 'compiler') + index = index_by(specs, ('architecture', 'compiler')) # Traverse the index and print out each package - for architecture in index: - tty.hline(architecture, char='=', color=spack.spec.architecture_color) - for compiler in index[architecture]: - tty.hline(compiler, char='-', color=spack.spec.compiler_color) - - specs = index[architecture][compiler] - specs.sort() - - abbreviated = [s.format('$_$@$+$#', color=True) for s in specs] - - if args.paths: - # Print one spec per line along with prefix path - width = max(len(s) for s in abbreviated) - width += 2 - format = " %-{}s%s".format(width) - - for abbrv, spec in zip(abbreviated, specs): - print format % (abbrv, spec.prefix) - - elif args.full_specs: - for spec in specs: - print spec.tree(indent=4, format='$_$@$+', color=True), - else: - max_len = max([len(s.name) for s in specs]) - max_len += 4 - - for spec in specs: - format = '$-' + str(max_len) + '_$@$+$#' - print " " + spec.format(format, color=True) + for i, (architecture, compiler) in enumerate(sorted(index)): + if i > 0: print + tty.hline("%s / %s" % (compiler, architecture), char='-') + + specs = index[(architecture, compiler)] + specs.sort() + + abbreviated = [s.format('$_$@$+$#', color=True) for s in specs] + + if args.paths: + # Print one spec per line along with prefix path + width = max(len(s) for s in abbreviated) + width += 2 + format = " %-{}s%s".format(width) + + for abbrv, spec in zip(abbreviated, specs): + print format % (abbrv, spec.prefix) + + elif args.full_specs: + for spec in specs: + print spec.tree(indent=4, format='$_$@$+', color=True), + else: + max_len = max([len(s.name) for s in specs]) + max_len += 4 + + colify((s.format('$-_$@$+$#') for s in specs), decorator=spack.spec.colorize_spec) -- cgit v1.2.3-70-g09d2 From 11cffff943b04ce69692db973c48de29647c8087 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Tue, 2 Dec 2014 21:56:22 -0800 Subject: colify handles ansi color input directly; no more decorator. --- lib/spack/llnl/util/tty/__init__.py | 25 ++++++++----------- lib/spack/llnl/util/tty/colify.py | 50 ++++++++++++++++++++----------------- lib/spack/llnl/util/tty/color.py | 5 ++++ lib/spack/spack/cmd/compiler.py | 10 +++++--- lib/spack/spack/cmd/find.py | 14 +++++------ 5 files changed, 56 insertions(+), 48 deletions(-) (limited to 'lib') diff --git a/lib/spack/llnl/util/tty/__init__.py b/lib/spack/llnl/util/tty/__init__.py index 5eeab67d6f..aba9e61f4f 100644 --- a/lib/spack/llnl/util/tty/__init__.py +++ b/lib/spack/llnl/util/tty/__init__.py @@ -145,18 +145,16 @@ def get_yes_or_no(prompt, **kwargs): def hline(label=None, **kwargs): - """Draw an optionally colored or labeled horizontal line. + """Draw a labeled horizontal line. Options: - char Char to draw the line with. Default '-' - color Color of the label. Default is no color. max_width Maximum width of the line. Default is 64 chars. - - See tty.color for possible color formats. """ - char = kwargs.get('char', '-') - color = kwargs.get('color', '') - max_width = kwargs.get('max_width', 64) + char = kwargs.pop('char', '-') + max_width = kwargs.pop('max_width', 64) + if kwargs: + raise TypeError("'%s' is an invalid keyword argument for this function." + % next(kwargs.iterkeys())) rows, cols = terminal_size() if not cols: @@ -166,15 +164,12 @@ def hline(label=None, **kwargs): cols = min(max_width, cols) label = str(label) - prefix = char * 2 + " " + label + " " - suffix = (cols - len(prefix)) * char + prefix = char * 2 + " " + suffix = " " + (cols - len(prefix) - clen(label)) * char out = StringIO() - if color: - prefix = char * 2 + " " + color + cescape(label) + "@. " - cwrite(prefix, stream=out, color=True) - else: - out.write(prefix) + out.write(prefix) + out.write(label) out.write(suffix) print out.getvalue() diff --git a/lib/spack/llnl/util/tty/colify.py b/lib/spack/llnl/util/tty/colify.py index 5c5c6077ec..6b2909990c 100644 --- a/lib/spack/llnl/util/tty/colify.py +++ b/lib/spack/llnl/util/tty/colify.py @@ -33,6 +33,7 @@ import struct from StringIO import StringIO from llnl.util.tty import terminal_size +from llnl.util.tty.color import clen class ColumnConfig: @@ -40,7 +41,8 @@ class ColumnConfig: self.cols = cols self.line_length = 0 self.valid = True - self.widths = [0] * cols + self.widths = [0] * cols # does not include ansi colors + self.cwidths = [0] * cols # includes ansi colors def __repr__(self): attrs = [(a,getattr(self, a)) for a in dir(self) if not a.startswith("__")] @@ -62,7 +64,10 @@ def config_variable_cols(elts, console_width, padding, cols=0): raise ValueError("cols must be non-negative.") # Get a bound on the most columns we could possibly have. - lengths = [len(elt) for elt in elts] + # 'clen' ignores length of ansi color sequences. + lengths = [clen(e) for e in elts] + clengths = [len(e) for e in elts] + max_cols = max(1, console_width / (min(lengths) + padding)) max_cols = min(len(elts), max_cols) @@ -71,17 +76,16 @@ def config_variable_cols(elts, console_width, padding, cols=0): # Determine the most columns possible for the console width. configs = [ColumnConfig(c) for c in col_range] - for elt, length in enumerate(lengths): + for i, length in enumerate(lengths): for conf in configs: if conf.valid: - col = elt / ((len(elts) + conf.cols - 1) / conf.cols) - padded = length - if col < (conf.cols - 1): - padded += padding - - if conf.widths[col] < padded: - conf.line_length += padded - conf.widths[col] - conf.widths[col] = padded + col = i / ((len(elts) + conf.cols - 1) / conf.cols) + p = padding if col < (conf.cols - 1) else 0 + + if conf.widths[col] < (length + p): + conf.line_length += length + p - conf.widths[col] + conf.widths[col] = length + p + conf.cwidths[col] = clengths[i] + p conf.valid = (conf.line_length < console_width) try: @@ -105,12 +109,17 @@ def config_uniform_cols(elts, console_width, padding, cols=0): if cols < 0: raise ValueError("cols must be non-negative.") - max_len = max(len(elt) for elt in elts) + padding + # 'clen' ignores length of ansi color sequences. + max_len = max(clen(e) for e in elts) + padding + max_clen = max(len(e) for e in elts) + padding if cols == 0: cols = max(1, console_width / max_len) cols = min(len(elts), cols) + config = ColumnConfig(cols) config.widths = [max_len] * cols + config.cwidths = [max_clen] * cols + return config @@ -139,9 +148,8 @@ def colify(elts, **options): Variable-width columns are tighter, uniform columns are all the same width and fit less data on the screen. - decorator= Function to add decoration (such as color) after columns have - already been fitted. Useful for fitting based only on - positive-width characters. + len= Function to use for calculating string length. + Useful for ignoring ansi color. Default is 'len'. """ # Get keyword arguments or set defaults cols = options.pop("cols", 0) @@ -151,7 +159,6 @@ def colify(elts, **options): tty = options.pop('tty', None) method = options.pop("method", "variable") console_cols = options.pop("width", None) - decorator = options.pop("decorator", lambda x:x) if options: raise TypeError("'%s' is an invalid keyword argument for this function." @@ -162,13 +169,10 @@ def colify(elts, **options): if not elts: return (0, ()) + # Use only one column if not a tty. if not tty: if tty is False or not output.isatty(): - for elt in elts: - output.write("%s\n" % elt) - - maxlen = max(len(str(s)) for s in elts) - return (1, (maxlen,)) + cols = 1 # Specify the number of character columns to use. if not console_cols: @@ -186,7 +190,7 @@ def colify(elts, **options): raise ValueError("method must be one of: " + allowed_methods) cols = config.cols - formats = ["%%-%ds" % width for width in config.widths[:-1]] + formats = ["%%-%ds" % width for width in config.cwidths[:-1]] formats.append("%s") # last column has no trailing space rows = (len(elts) + cols - 1) / cols @@ -196,7 +200,7 @@ def colify(elts, **options): output.write(" " * indent) for col in xrange(cols): elt = col * rows + row - output.write(decorator(formats[col] % elts[elt])) + output.write(formats[col] % elts[elt]) output.write("\n") row += 1 diff --git a/lib/spack/llnl/util/tty/color.py b/lib/spack/llnl/util/tty/color.py index 14974a1014..598e9d44f5 100644 --- a/lib/spack/llnl/util/tty/color.py +++ b/lib/spack/llnl/util/tty/color.py @@ -149,6 +149,11 @@ def colorize(string, **kwargs): return re.sub(color_re, match_to_ansi(color), string) +def clen(string): + """Return the length of a string, excluding ansi color sequences.""" + return len(re.sub(r'\033[^m]*m', '', string)) + + def cwrite(string, stream=sys.stdout, color=None): """Replace all color expressions in string with ANSI control codes and write the result to the stream. If color is diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py index 5c46a3536d..e37f44b3b7 100644 --- a/lib/spack/spack/cmd/compiler.py +++ b/lib/spack/spack/cmd/compiler.py @@ -25,6 +25,7 @@ from external import argparse import llnl.util.tty as tty +from llnl.util.tty.color import colorize from llnl.util.tty.colify import colify from llnl.util.lang import index_by @@ -96,9 +97,12 @@ def compiler_info(args): def compiler_list(args): tty.msg("Available compilers") index = index_by(spack.compilers.all_compilers(), 'name') - for name, compilers in index.items(): - tty.hline(name, char='-', color=spack.spec.compiler_color) - colify(reversed(sorted(compilers)), indent=4) + for i, (name, compilers) in enumerate(index.items()): + if i >= 1: print + + cname = "%s{%s}" % (spack.spec.compiler_color, name) + tty.hline(colorize(cname), char='-') + colify(reversed(sorted(compilers))) def compiler(parser, args): diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index c0a3162429..b6efc980b6 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -79,13 +79,16 @@ def find(parser, args): # Traverse the index and print out each package for i, (architecture, compiler) in enumerate(sorted(index)): if i > 0: print - tty.hline("%s / %s" % (compiler, architecture), char='-') - specs = index[(architecture, compiler)] + 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('$_$@$+$#', color=True) for s in specs] - if args.paths: # Print one spec per line along with prefix path width = max(len(s) for s in abbreviated) @@ -99,7 +102,4 @@ def find(parser, args): for spec in specs: print spec.tree(indent=4, format='$_$@$+', color=True), else: - max_len = max([len(s.name) for s in specs]) - max_len += 4 - - colify((s.format('$-_$@$+$#') for s in specs), decorator=spack.spec.colorize_spec) + colify(s.format('$-_$@$+$#', color=True) for s in specs) -- cgit v1.2.3-70-g09d2 From fdc6081244d0d95793c23a5295edc580096ad351 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Tue, 2 Dec 2014 22:52:53 -0800 Subject: CLI improvements to find and list. --- lib/spack/spack/cmd/find.py | 16 +++++++++++----- lib/spack/spack/cmd/list.py | 1 - 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index b6efc980b6..1de3413d42 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -40,12 +40,15 @@ description ="Find installed spack packages" def setup_parser(subparser): format_group = subparser.add_mutually_exclusive_group() + format_group.add_argument( + '-l', '--long', action='store_true', dest='long', + help='Show dependency hashes as well as versions.') format_group.add_argument( '-p', '--paths', action='store_true', dest='paths', help='Show paths to package install directories') format_group.add_argument( - '-l', '--long', action='store_true', dest='full_specs', - help='Show full-length specs of installed packages') + '-d', '--deps', action='store_true', dest='full_deps', + help='Show full dependency DAG of installed packages') subparser.add_argument( 'query_specs', nargs=argparse.REMAINDER, @@ -88,7 +91,7 @@ def find(parser, args): specs = index[(architecture,compiler)] specs.sort() - abbreviated = [s.format('$_$@$+$#', color=True) for s in specs] + abbreviated = [s.format('$_$@$+', color=True) for s in specs] if args.paths: # Print one spec per line along with prefix path width = max(len(s) for s in abbreviated) @@ -98,8 +101,11 @@ def find(parser, args): for abbrv, spec in zip(abbreviated, specs): print format % (abbrv, spec.prefix) - elif args.full_specs: + elif args.full_deps: for spec in specs: print spec.tree(indent=4, format='$_$@$+', color=True), else: - colify(s.format('$-_$@$+$#', color=True) for s in specs) + fmt = '$-_$@$+' + if args.long: + fmt += '$#' + colify(s.format(fmt, color=True) for s in specs) diff --git a/lib/spack/spack/cmd/list.py b/lib/spack/spack/cmd/list.py index 5c7051d6a9..1f0978a18e 100644 --- a/lib/spack/spack/cmd/list.py +++ b/lib/spack/spack/cmd/list.py @@ -61,5 +61,4 @@ def list(parser, args): indent=0 if sys.stdout.isatty(): tty.msg("%d packages." % len(sorted_packages)) - indent=2 colify(sorted_packages, indent=indent) -- cgit v1.2.3-70-g09d2