diff -r 3be84ba08176 -r 330443795456 src/modules/misc.py --- a/src/modules/misc.py Fri Jul 08 18:16:51 2011 -0700 +++ b/src/modules/misc.py Fri Jul 08 22:26:37 2011 -0700 @@ -30,10 +30,10 @@ import getopt import hashlib import locale -import operator import os import pkg.client.api_errors as api_errors import pkg.portable as portable +import pkg.version as version import platform import re import shutil @@ -1013,7 +1013,8 @@ # check for duplicate options if k in opts_seen and (type(v) != list and type(v) != int): if opt == opts_seen[k]: - opts_err_repeated(opt, op) + usage_cb(_("option '%s' repeated") % opt, + cmd=op) usage_cb(_("'%s' and '%s' have the same meaning") % (opts_seen[k], opt), cmd=op) opts_seen[k] = opt @@ -1083,3 +1084,142 @@ ) except EnvironmentError, e: raise apx._convert_error(e) + +def get_col_listing(desired_field_order, field_data, field_values, out_format, + def_fmt, omit_headers, escape_output=True): + """Returns a string containing a columnar listing defined by provided + values. + + 'desired_field_order' is the list of the fields to show in the order + they should be output left to right. + + 'field_data' is a dictionary of lists of the form: + { + field_name1: { + [(output formats), field header, initial field value] + }, + field_nameN: { + [(output formats), field header, initial field value] + } + } + + 'field_values' is a generator or list of dictionaries of the form: + { + field_name1: field_value, + field_nameN: field_value + } + + 'out_format' is the format to use for output. Currently 'default' and + 'tsv' are supported. The first is intended for human-readable output, + the latter for parseable output. + + 'def_fmt' is the default Python formatting string to use for output. It + must match the fields defined in 'field_data'. + + 'omit_headers' is a boolean specifying whether headers should be + included in the listing. + + 'escape_output' is an optional boolean indicating whether shell + metacharacters or embedded control sequences should be escaped + before display. + """ + + # Custom sort function for preserving field ordering + def sort_fields(one, two): + return desired_field_order.index(get_header(one)) - \ + desired_field_order.index(get_header(two)) + + # Functions for manipulating field_data records + def filter_default(record): + return "default" in record[0] + + def filter_tsv(record): + return "tsv" in record[0] + + def get_header(record): + return record[1] + + def get_value(record): + return record[2] + + def quote_value(val): + if out_format == "tsv": + # Expand tabs if tsv output requested. + val = val.replace("\t", " " * 8) + nval = val + # Escape bourne shell metacharacters. + for c in ("\\", " ", "\t", "\n", "'", "`", ";", "&", "(", ")", + "|", "^", "<", ">"): + nval = nval.replace(c, "\\" + c) + return nval + + def set_value(entry): + val = entry[1] + multi_value = False + if isinstance(val, (list, set)): + multi_value = True + elif val == "": + entry[0][2] = '""' + return + elif val is None: + entry[0][2] = '' + return + else: + val = [val] + + nval = [] + for v in val: + if v == "": + # Indicate empty string value using "". + nval.append('""') + elif v is None: + # Indicate no value using empty string. + nval.append('') + elif escape_output: + # Otherwise, escape the value to be displayed. + nval.append(quote_value(str(v))) + else: + # Caller requested value not be escaped. + nval.append(str(v)) + + val = " ".join(nval) + nval = None + if multi_value: + val = "(%s)" % val + entry[0][2] = val + + if out_format == "default": + # Create a formatting string for the default output + # format. + fmt = def_fmt + filter_func = filter_default + elif out_format == "tsv": + # Create a formatting string for the tsv output + # format. + num_fields = len(field_data.keys()) + fmt = "\t".join('%s' for x in xrange(num_fields)) + filter_func = filter_tsv + + # Extract the list of headers from the field_data dictionary. Ensure + # they are extracted in the desired order by using the custom sort + # function. + hdrs = map(get_header, sorted(filter(filter_func, field_data.values()), + sort_fields)) + + # Output a header if desired. + output = "" + if not omit_headers: + output += fmt % tuple(hdrs) + output += "\n" + + for entry in field_values: + map(set_value, ( + (field_data[f], v) + for f, v in entry.iteritems() + )) + values = map(get_value, sorted(filter(filter_func, + field_data.values()), sort_fields)) + output += fmt % tuple(values) + output += "\n" + + return output