diff -r c744f23af6c0 -r f48530fd135d src/modules/misc.py --- a/src/modules/misc.py Fri Aug 12 15:46:57 2011 -0700 +++ b/src/modules/misc.py Mon Aug 15 15:10:42 2011 -0700 @@ -31,12 +31,10 @@ import hashlib import locale 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 +import simplejson as json import stat import struct import sys @@ -45,6 +43,9 @@ import urlparse import zlib +import pkg.client.api_errors as api_errors +import pkg.portable as portable + from pkg import VERSION from pkg.client import global_settings from pkg.client.debugvalues import DebugValues @@ -1083,12 +1084,12 @@ for fname in fnames ) except EnvironmentError, e: - raise apx._convert_error(e) + raise api_errors._convert_error(e) -def get_col_listing(desired_field_order, field_data, field_values, out_format, +def get_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. + """Returns a string containing a listing defined by provided values + in the specified output format. 'desired_field_order' is the list of the fields to show in the order they should be output left to right. @@ -1109,19 +1110,22 @@ 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. + 'out_format' is the format to use for output. Currently 'default', + 'tsv', 'json', and 'json-formatted' are supported. The first is + intended for columnar, human-readable output, and the others for + parsable output. - 'def_fmt' is the default Python formatting string to use for output. It - must match the fields defined in 'field_data'. + 'def_fmt' is the default Python formatting string to use for the + 'default' human-readable output. It must match the fields defined + in 'field_data'. 'omit_headers' is a boolean specifying whether headers should be - included in the listing. + included in the listing. (If applicable to the specified output + format.) 'escape_output' is an optional boolean indicating whether shell metacharacters or embedded control sequences should be escaped - before display. + before display. (If applicable to the specified output format.) """ # Custom sort function for preserving field ordering @@ -1156,7 +1160,7 @@ def set_value(entry): val = entry[1] multi_value = False - if isinstance(val, (list, set)): + if isinstance(val, (list, tuple, set, frozenset)): multi_value = True elif val == "": entry[0][2] = '""' @@ -1196,9 +1200,44 @@ elif out_format == "tsv": # Create a formatting string for the tsv output # format. - num_fields = len(field_data.keys()) + num_fields = sum( + 1 for k in field_data + if filter_tsv(field_data[k]) + ) fmt = "\t".join('%s' for x in xrange(num_fields)) filter_func = filter_tsv + elif out_format == "json" or out_format == "json-formatted": + args = { "sort_keys": True } + if out_format == "json-formatted": + args["indent"] = 2 + + # 'json' formats always include any extra fields returned; + # any explicitly named fields are only included if 'json' + # is explicitly listed. + def fmt_val(v): + if isinstance(v, basestring): + return v + if isinstance(v, (list, tuple, set, frozenset)): + return [fmt_val(e) for e in v] + if isinstance(v, dict): + for k, e in v.items(): + v[k] = fmt_val(e) + return v + return str(v) + + output = json.dumps([ + dict( + (k, fmt_val(entry[k])) + for k in entry + if k not in field_data or "json" in field_data[k][0] + ) + for entry in field_values + ], **args) + + if out_format == "json-formatted": + # Include a trailing newline for readability. + return output + "\n" + return output # Extract the list of headers from the field_data dictionary. Ensure # they are extracted in the desired order by using the custom sort @@ -1216,6 +1255,7 @@ map(set_value, ( (field_data[f], v) for f, v in entry.iteritems() + if f in field_data )) values = map(get_value, sorted(filter(filter_func, field_data.values()), sort_fields))