Repository: cassandra
Updated Branches:
  refs/heads/trunk 11910c6c9 -> 1de4a983b


(cqlsh) interpret CQL type for formatting blobs

patch by Stefania Alborghetti; reviewed by Adam Holmberg for CASSANDRA-11274


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1de4a983
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1de4a983
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1de4a983

Branch: refs/heads/trunk
Commit: 1de4a983b2b5dbc8a87d04e25124ad03f6133a1c
Parents: 11910c6
Author: Stefania Alborghetti <[email protected]>
Authored: Mon Feb 29 10:05:59 2016 +0800
Committer: Aleksey Yeschenko <[email protected]>
Committed: Fri Mar 11 00:41:14 2016 +0000

----------------------------------------------------------------------
 CHANGES.txt                  |   1 +
 bin/cqlsh.py                 |  75 ++++--------------
 pylib/cqlshlib/copyutil.py   |  27 ++++---
 pylib/cqlshlib/formatting.py | 161 +++++++++++++++++++++++++++++++-------
 pylib/cqlshlib/tracing.py    |   1 +
 5 files changed, 167 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1de4a983/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 3682647..8178555 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.6
+ * (cqlsh) interpret CQL type for formatting blobs (CASSANDRA-11274)
  * Refuse to start and print txn log information in case of disk
    corruption (CASSANDRA-10112)
  * Resolve some eclipse-warnings (CASSANDRA-11086)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1de4a983/bin/cqlsh.py
----------------------------------------------------------------------
diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index e85c94d..f8cd32d 100644
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -36,7 +36,6 @@ import codecs
 import ConfigParser
 import csv
 import getpass
-import locale
 import optparse
 import os
 import platform
@@ -157,7 +156,6 @@ from cassandra.metadata import (ColumnMetadata, 
KeyspaceMetadata,
                                 TableMetadata, protect_name, protect_names)
 from cassandra.policies import WhiteListRoundRobinPolicy
 from cassandra.query import SimpleStatement, ordered_dict_factory, 
TraceUnavailable
-from cassandra.type_codes import DateType
 from cassandra.util import datetime_from_timestamp
 
 # cqlsh should run correctly when run out of a Cassandra source tree,
@@ -171,9 +169,8 @@ from cqlshlib.copyutil import ExportTask, ImportTask
 from cqlshlib.displaying import (ANSI_RESET, BLUE, COLUMN_NAME_COLORS, CYAN,
                                  RED, WHITE, FormattedValue, colorme)
 from cqlshlib.formatting import (DEFAULT_DATE_FORMAT, DEFAULT_NANOTIME_FORMAT,
-                                 DEFAULT_TIMESTAMP_FORMAT, DateTimeFormat,
-                                 format_by_type, format_value_utype,
-                                 formatter_for)
+                                 DEFAULT_TIMESTAMP_FORMAT, CqlType, 
DateTimeFormat,
+                                 format_by_type, formatter_for)
 from cqlshlib.tracing import print_trace, print_trace_session
 from cqlshlib.util import get_file_encoding_bomsize, trim_if_present
 
@@ -578,14 +575,14 @@ def full_cql_version(ver):
     return ver, vertuple
 
 
-def format_value(val, output_encoding, addcolor=False, date_time_format=None,
+def format_value(val, cqltype, encoding, addcolor=False, date_time_format=None,
                  float_precision=None, colormap=None, nullval=None):
     if isinstance(val, DecodeError):
         if addcolor:
             return colorme(repr(val.thebytes), colormap, 'error')
         else:
             return FormattedValue(repr(val.thebytes))
-    return format_by_type(type(val), val, output_encoding, colormap=colormap,
+    return format_by_type(val, cqltype=cqltype, encoding=encoding, 
colormap=colormap,
                           addcolor=addcolor, nullval=nullval, 
date_time_format=date_time_format,
                           float_precision=float_precision)
 
@@ -602,30 +599,6 @@ warnings.filterwarnings('always', 
category=cql3handling.UnexpectedTableStructure
 
 
 def insert_driver_hooks():
-    extend_cql_deserialization()
-    auto_format_udts()
-
-
-def extend_cql_deserialization():
-    """
-    The python driver returns BLOBs as string, but we expect them as bytearrays
-    the implementation of cassandra.cqltypes.BytesType.deserialize.
-
-    The deserializers package exists only when the driver has been compiled 
with cython extensions and
-    cassandra.deserializers.DesBytesType replaces 
cassandra.cqltypes.BytesType.deserialize.
-
-    DesBytesTypeByteArray is a fast deserializer that converts blobs into 
bytearrays but it was
-    only introduced recently (3.1.0). If it is available we use it, otherwise 
we remove
-    cassandra.deserializers.DesBytesType so that we fall back onto 
cassandra.cqltypes.BytesType.deserialize
-    just like in the case where no cython extensions are present.
-    """
-    if hasattr(cassandra, 'deserializers'):
-        if hasattr(cassandra.deserializers, 'DesBytesTypeByteArray'):
-            cassandra.deserializers.DesBytesType = 
cassandra.deserializers.DesBytesTypeByteArray
-        else:
-            del cassandra.deserializers.DesBytesType
-
-    cassandra.cqltypes.BytesType.deserialize = staticmethod(lambda byts, 
protocol_version: bytearray(byts))
 
     class DateOverFlowWarning(RuntimeWarning):
         pass
@@ -636,7 +609,8 @@ def extend_cql_deserialization():
         try:
             return datetime_from_timestamp(timestamp_ms / 1000.0)
         except OverflowError:
-            warnings.warn(DateOverFlowWarning("Some timestamps are larger than 
Python datetime can represent. Timestamps are displayed in milliseconds from 
epoch."))
+            warnings.warn(DateOverFlowWarning("Some timestamps are larger than 
Python datetime can represent. "
+                                              "Timestamps are displayed in 
milliseconds from epoch."))
             return timestamp_ms
 
     cassandra.cqltypes.DateType.deserialize = 
staticmethod(deserialize_date_fallback_int)
@@ -648,27 +622,6 @@ def extend_cql_deserialization():
     cassandra.cqltypes.CassandraType.support_empty_values = True
 
 
-def auto_format_udts():
-    # when we see a new user defined type, set up the shell formatting for it
-    udt_apply_params = cassandra.cqltypes.UserType.apply_parameters
-
-    def new_apply_params(cls, *args, **kwargs):
-        udt_class = udt_apply_params(*args, **kwargs)
-        formatter_for(udt_class.typename)(format_value_utype)
-        return udt_class
-
-    cassandra.cqltypes.UserType.udt_apply_parameters = 
classmethod(new_apply_params)
-
-    make_udt_class = cassandra.cqltypes.UserType.make_udt_class
-
-    def new_make_udt_class(cls, *args, **kwargs):
-        udt_class = make_udt_class(*args, **kwargs)
-        formatter_for(udt_class.tuple_type.__name__)(format_value_utype)
-        return udt_class
-
-    cassandra.cqltypes.UserType.make_udt_class = 
classmethod(new_make_udt_class)
-
-
 class FrozenType(cassandra.cqltypes._ParameterizedType):
     """
     Needed until the bundled python driver adds FrozenType.
@@ -838,20 +791,20 @@ class Shell(cmd.Cmd):
     def cqlver_atleast(self, major, minor=0, patch=0):
         return self.cql_ver_tuple[:3] >= (major, minor, patch)
 
-    def myformat_value(self, val, **kwargs):
+    def myformat_value(self, val, cqltype=None, **kwargs):
         if isinstance(val, DecodeError):
             self.decoding_errors.append(val)
         try:
             dtformats = 
DateTimeFormat(timestamp_format=self.display_timestamp_format,
                                        date_format=self.display_date_format, 
nanotime_format=self.display_nanotime_format,
                                        timezone=self.display_timezone)
-            return format_value(val, self.output_codec.name,
+            return format_value(val, cqltype=cqltype, 
encoding=self.output_codec.name,
                                 addcolor=self.color, 
date_time_format=dtformats,
                                 float_precision=self.display_float_precision, 
**kwargs)
         except Exception, e:
             err = FormatError(val, e)
             self.decoding_errors.append(err)
-            return format_value(err, self.output_codec.name, 
addcolor=self.color)
+            return format_value(err, cqltype=cqltype, 
encoding=self.output_codec.name, addcolor=self.color)
 
     def myformat_colname(self, name, table_meta=None):
         column_colors = COLUMN_NAME_COLORS.copy()
@@ -1376,8 +1329,15 @@ class Shell(cmd.Cmd):
         if not column_names and not table_meta:
             return
         column_names = column_names or table_meta.columns.keys()
+
+        cql_types = []
+        if table_meta:
+            ks_meta = self.conn.metadata.keyspaces[table_meta.keyspace_name]
+            cql_types = [CqlType(table_meta.columns[c].cql_type, ks_meta)
+                         if c in table_meta.columns else None for c in 
column_names]
+
         formatted_names = [self.myformat_colname(name, table_meta) for name in 
column_names]
-        formatted_values = [map(self.myformat_value, row.values()) for row in 
rows]
+        formatted_values = [map(self.myformat_value, row.values(), cql_types) 
for row in rows]
 
         if self.expand_enabled:
             self.print_formatted_result_vertically(formatted_names, 
formatted_values)
@@ -1656,7 +1616,6 @@ class Shell(cmd.Cmd):
         except KeyError:
             raise UserTypeNotFound("User type %r not found" % typename)
         print usertype.export_as_string()
-        print
 
     def describe_cluster(self):
         print '\nCluster: %s' % self.get_cluster_name()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1de4a983/pylib/cqlshlib/copyutil.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/copyutil.py b/pylib/cqlshlib/copyutil.py
index e690e82..ef96a92 100644
--- a/pylib/cqlshlib/copyutil.py
+++ b/pylib/cqlshlib/copyutil.py
@@ -51,7 +51,7 @@ from cassandra.util import Date, Time
 
 from cql3handling import CqlRuleSet
 from displaying import NO_COLOR_MAP
-from formatting import format_value_default, DateTimeFormat, EMPTY, 
get_formatter
+from formatting import format_value_default, CqlType, DateTimeFormat, EMPTY, 
get_formatter
 from sslhandling import ssl_settings
 
 PROFILE_ON = False
@@ -1463,12 +1463,17 @@ class ExportProcess(ChildProcess):
         return session
 
     def attach_callbacks(self, token_range, future, session):
+        metadata = session.cluster.metadata
+        ks_meta = metadata.keyspaces[self.ks]
+        table_meta = ks_meta.tables[self.table]
+        cql_types = [CqlType(table_meta.columns[c].cql_type, ks_meta) for c in 
self.columns]
+
         def result_callback(rows):
             if future.has_more_pages:
                 future.start_fetching_next_page()
-                self.write_rows_to_csv(token_range, rows)
+                self.write_rows_to_csv(token_range, rows, cql_types)
             else:
-                self.write_rows_to_csv(token_range, rows)
+                self.write_rows_to_csv(token_range, rows, cql_types)
                 self.outmsg.send((None, None))
                 session.complete_request()
 
@@ -1478,7 +1483,7 @@ class ExportProcess(ChildProcess):
 
         future.add_callbacks(callback=result_callback, errback=err_callback)
 
-    def write_rows_to_csv(self, token_range, rows):
+    def write_rows_to_csv(self, token_range, rows, cql_types):
         if not rows:
             return  # no rows in this range
 
@@ -1487,7 +1492,7 @@ class ExportProcess(ChildProcess):
             writer = csv.writer(output, **self.options.dialect)
 
             for row in rows:
-                writer.writerow(map(self.format_value, row))
+                writer.writerow(map(self.format_value, row, cql_types))
 
             data = (output.getvalue(), len(rows))
             self.outmsg.send((token_range, data))
@@ -1496,17 +1501,17 @@ class ExportProcess(ChildProcess):
         except Exception, e:
             self.report_error(e, token_range)
 
-    def format_value(self, val):
+    def format_value(self, val, cqltype):
         if val is None or val == EMPTY:
             return format_value_default(self.nullval, colormap=NO_COLOR_MAP)
 
-        ctype = type(val)
-        formatter = self.formatters.get(ctype, None)
+        formatter = self.formatters.get(cqltype, None)
         if not formatter:
-            formatter = get_formatter(ctype)
-            self.formatters[ctype] = formatter
+            formatter = get_formatter(val, cqltype)
+            self.formatters[cqltype] = formatter
 
-        return formatter(val, encoding=self.encoding, colormap=NO_COLOR_MAP, 
date_time_format=self.date_time_format,
+        return formatter(val, cqltype=cqltype,
+                         encoding=self.encoding, colormap=NO_COLOR_MAP, 
date_time_format=self.date_time_format,
                          float_precision=self.float_precision, 
nullval=self.nullval, quote=False,
                          decimal_sep=self.decimal_sep, 
thousands_sep=self.thousands_sep,
                          boolean_styles=self.boolean_styles)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1de4a983/pylib/cqlshlib/formatting.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/formatting.py b/pylib/cqlshlib/formatting.py
index f88d936..aa50204 100644
--- a/pylib/cqlshlib/formatting.py
+++ b/pylib/cqlshlib/formatting.py
@@ -61,7 +61,7 @@ default_colormap = DEFAULT_VALUE_COLORS
 empty_colormap = defaultdict(lambda: '')
 
 
-def format_by_type(cqltype, val, encoding, colormap=None, addcolor=False,
+def format_by_type(val, cqltype, encoding, colormap=None, addcolor=False,
                    nullval=None, date_time_format=None, float_precision=None,
                    decimal_sep=None, thousands_sep=None, boolean_styles=None):
     if nullval is None:
@@ -76,7 +76,7 @@ def format_by_type(cqltype, val, encoding, colormap=None, 
addcolor=False,
         date_time_format = DateTimeFormat()
     if float_precision is None:
         float_precision = default_float_precision
-    return format_value(cqltype, val, encoding=encoding, colormap=colormap,
+    return format_value(val, cqltype=cqltype, encoding=encoding, 
colormap=colormap,
                         date_time_format=date_time_format, 
float_precision=float_precision,
                         nullval=nullval, decimal_sep=decimal_sep, 
thousands_sep=thousands_sep,
                         boolean_styles=boolean_styles)
@@ -117,6 +117,87 @@ class DateTimeFormat:
         self.timezone = timezone
 
 
+class CqlType(object):
+    """
+    A class for converting a string into a cql type name that can match a 
formatter
+    and a list of its sub-types, if any.
+    """
+    pattern = re.compile('^([^<]*)<(.*)>$')  # *<*>
+
+    def __init__(self, typestring, ksmeta=None):
+        self.type_name, self.sub_types, self.formatter = 
self.parse(typestring, ksmeta)
+
+    def __str__(self):
+        return "%s%s" % (self.type_name, self.sub_types or '')
+
+    __repr__ = __str__
+
+    def get_n_sub_types(self, num):
+        """
+        Return the sub-types if the requested number matches the length of the 
sub-types (tuples)
+        or the first sub-type times the number requested if the length of the 
sub-types is one (list, set),
+        otherwise raise an exception
+        """
+        if len(self.sub_types) == num:
+            return self.sub_types
+        elif len(self.sub_types) == 1:
+            return [self.sub_types[0]] * num
+        else:
+            raise Exception("Unexpected number of subtypes %d - %s" % (num, 
self.sub_types))
+
+    def parse(self, typestring, ksmeta):
+        """
+        Parse the typestring by looking at this pattern: *<*>. If there is no 
match then the type
+        is either a simple type or a user type, otherwise it must be a 
composite type
+        for which we need to look-up the sub-types. For user types the sub 
types can be extracted
+        from the keyspace metadata.
+        """
+        while True:
+            m = self.pattern.match(typestring)
+            if not m:  # no match, either a simple or a user type
+                name = typestring
+                if ksmeta and name in ksmeta.user_types:  # a user type, look 
at ks meta for sub types
+                    sub_types = [CqlType(t, ksmeta) for t in 
ksmeta.user_types[name].field_types]
+                    return name, sub_types, format_value_utype
+                else:
+                    return name, [], self._get_formatter(name)
+            else:
+                if m.group(1) == 'frozen':  # ignore frozen<>
+                    typestring = m.group(2)
+                    continue
+
+                name = m.group(1)  # a composite type, parse sub types
+                return name, self.parse_sub_types(m.group(2), ksmeta), 
self._get_formatter(name)
+
+    @staticmethod
+    def _get_formatter(name):
+        return _formatters.get(name.lower())
+
+    @staticmethod
+    def parse_sub_types(val, ksmeta):
+        """
+        Split val into sub-strings separated by commas but only if not within 
a <> pair
+        Return a list of CqlType instances where each instance is initialized 
with the sub-strings
+        that were found.
+        """
+        last = 0
+        level = 0
+        ret = []
+        for i, c in enumerate(val):
+            if c == '<':
+                level += 1
+            elif c == '>':
+                level -= 1
+            elif c == ',' and level == 0:
+                ret.append(val[last:i].strip())
+                last = i + 1
+
+        if last < len(val) - 1:
+            ret.append(val[last:].strip())
+
+        return [CqlType(r, ksmeta) for r in ret]
+
+
 def format_value_default(val, colormap, **_):
     val = str(val)
     escapedval = val.replace('\\', '\\\\')
@@ -128,20 +209,24 @@ def format_value_default(val, colormap, **_):
 _formatters = {}
 
 
-def format_value(type, val, **kwargs):
+def format_value(val, cqltype, **kwargs):
     if val == EMPTY:
         return format_value_default('', **kwargs)
-    formatter = _formatters.get(type.__name__, format_value_default)
-    return formatter(val, **kwargs)
 
+    formatter = get_formatter(val, cqltype)
+    return formatter(val, cqltype=cqltype, **kwargs)
 
-def get_formatter(type):
-    return _formatters.get(type.__name__, format_value_default)
+
+def get_formatter(val, cqltype):
+    if cqltype and cqltype.formatter:
+        return cqltype.formatter
+
+    return _formatters.get(type(val).__name__.lower(), format_value_default)
 
 
 def formatter_for(typname):
     def registrator(f):
-        _formatters[typname] = f
+        _formatters[typname.lower()] = f
         return f
     return registrator
 
@@ -151,6 +236,7 @@ def format_value_blob(val, colormap, **_):
     bval = '0x' + binascii.hexlify(val)
     return colorme(bval, colormap, 'blob')
 formatter_for('buffer')(format_value_blob)
+formatter_for('blob')(format_value_blob)
 
 
 def format_python_formatted_type(val, colormap, color, quote=False):
@@ -171,6 +257,8 @@ def format_value_decimal(val, float_precision, colormap, 
decimal_sep=None, thous
 def format_value_uuid(val, colormap, **_):
     return format_python_formatted_type(val, colormap, 'uuid')
 
+formatter_for('timeuuid')(format_value_uuid)
+
 
 @formatter_for('inet')
 def formatter_value_inet(val, colormap, quote=False, **_):
@@ -183,6 +271,8 @@ def format_value_boolean(val, colormap, 
boolean_styles=None, **_):
         val = boolean_styles[0] if val else boolean_styles[1]
     return format_python_formatted_type(val, colormap, 'boolean')
 
+formatter_for('boolean')(format_value_boolean)
+
 
 def format_floating_point_type(val, colormap, float_precision, 
decimal_sep=None, thousands_sep=None, **_):
     if math.isnan(val):
@@ -211,6 +301,7 @@ def format_floating_point_type(val, colormap, 
float_precision, decimal_sep=None,
     return colorme(bval, colormap, 'float')
 
 formatter_for('float')(format_floating_point_type)
+formatter_for('double')(format_floating_point_type)
 
 
 def format_integer_type(val, colormap, thousands_sep=None, **_):
@@ -234,18 +325,26 @@ else:
 
 formatter_for('long')(format_integer_type)
 formatter_for('int')(format_integer_type)
+formatter_for('bigint')(format_integer_type)
+formatter_for('varint')(format_integer_type)
 
 
 @formatter_for('datetime')
 def format_value_timestamp(val, colormap, date_time_format, quote=False, **_):
-    bval = strftime(date_time_format.timestamp_format,
-                    calendar.timegm(val.utctimetuple()),
-                    microseconds=val.microsecond,
-                    timezone=date_time_format.timezone)
+    if isinstance(val, datetime.datetime):
+        bval = strftime(date_time_format.timestamp_format,
+                        calendar.timegm(val.utctimetuple()),
+                        microseconds=val.microsecond,
+                        timezone=date_time_format.timezone)
+    else:
+        bval = str(val)
+
     if quote:
         bval = "'%s'" % bval
     return colorme(bval, colormap, 'timestamp')
 
+formatter_for('timestamp')(format_value_timestamp)
+
 
 def strftime(time_format, seconds, microseconds=0, timezone=None):
     ret_dt = datetime_from_timestamp(seconds) + 
datetime.timedelta(microseconds=microseconds)
@@ -279,16 +378,18 @@ def format_value_text(val, encoding, colormap, 
quote=False, **_):
 
 # name alias
 formatter_for('unicode')(format_value_text)
+formatter_for('text')(format_value_text)
+formatter_for('ascii')(format_value_text)
 
 
-def format_simple_collection(val, lbracket, rbracket, encoding,
+def format_simple_collection(val, cqltype, lbracket, rbracket, encoding,
                              colormap, date_time_format, float_precision, 
nullval,
                              decimal_sep, thousands_sep, boolean_styles):
-    subs = [format_value(type(sval), sval, encoding=encoding, 
colormap=colormap,
+    subs = [format_value(sval, cqltype=stype, encoding=encoding, 
colormap=colormap,
                          date_time_format=date_time_format, 
float_precision=float_precision,
                          nullval=nullval, quote=True, decimal_sep=decimal_sep,
                          thousands_sep=thousands_sep, 
boolean_styles=boolean_styles)
-            for sval in val]
+            for sval, stype in zip(val, cqltype.get_n_sub_types(len(val)))]
     bval = lbracket + ', '.join(get_str(sval) for sval in subs) + rbracket
     if colormap is NO_COLOR_MAP:
         return bval
@@ -301,25 +402,25 @@ def format_simple_collection(val, lbracket, rbracket, 
encoding,
 
 
 @formatter_for('list')
-def format_value_list(val, encoding, colormap, date_time_format, 
float_precision, nullval,
+def format_value_list(val, cqltype, encoding, colormap, date_time_format, 
float_precision, nullval,
                       decimal_sep, thousands_sep, boolean_styles, **_):
-    return format_simple_collection(val, '[', ']', encoding, colormap,
+    return format_simple_collection(val, cqltype, '[', ']', encoding, colormap,
                                     date_time_format, float_precision, nullval,
                                     decimal_sep, thousands_sep, boolean_styles)
 
 
 @formatter_for('tuple')
-def format_value_tuple(val, encoding, colormap, date_time_format, 
float_precision, nullval,
+def format_value_tuple(val, cqltype, encoding, colormap, date_time_format, 
float_precision, nullval,
                        decimal_sep, thousands_sep, boolean_styles, **_):
-    return format_simple_collection(val, '(', ')', encoding, colormap,
+    return format_simple_collection(val, cqltype, '(', ')', encoding, colormap,
                                     date_time_format, float_precision, nullval,
                                     decimal_sep, thousands_sep, boolean_styles)
 
 
 @formatter_for('set')
-def format_value_set(val, encoding, colormap, date_time_format, 
float_precision, nullval,
+def format_value_set(val, cqltype, encoding, colormap, date_time_format, 
float_precision, nullval,
                      decimal_sep, thousands_sep, boolean_styles, **_):
-    return format_simple_collection(sorted(val), '{', '}', encoding, colormap,
+    return format_simple_collection(sorted(val), cqltype, '{', '}', encoding, 
colormap,
                                     date_time_format, float_precision, nullval,
                                     decimal_sep, thousands_sep, boolean_styles)
 formatter_for('frozenset')(format_value_set)
@@ -328,15 +429,15 @@ formatter_for('SortedSet')(format_value_set)
 
 
 @formatter_for('dict')
-def format_value_map(val, encoding, colormap, date_time_format, 
float_precision, nullval,
+def format_value_map(val, cqltype, encoding, colormap, date_time_format, 
float_precision, nullval,
                      decimal_sep, thousands_sep, boolean_styles, **_):
-    def subformat(v):
-        return format_value(type(v), v, encoding=encoding, colormap=colormap,
+    def subformat(v, t):
+        return format_value(v, cqltype=t, encoding=encoding, colormap=colormap,
                             date_time_format=date_time_format, 
float_precision=float_precision,
                             nullval=nullval, quote=True, 
decimal_sep=decimal_sep,
                             thousands_sep=thousands_sep, 
boolean_styles=boolean_styles)
 
-    subs = [(subformat(k), subformat(v)) for (k, v) in sorted(val.items())]
+    subs = [(subformat(k, cqltype.sub_types[0]), subformat(v, 
cqltype.sub_types[1])) for (k, v) in sorted(val.items())]
     bval = '{' + ', '.join(get_str(k) + ': ' + get_str(v) for (k, v) in subs) 
+ '}'
     if colormap is NO_COLOR_MAP:
         return bval
@@ -351,14 +452,15 @@ def format_value_map(val, encoding, colormap, 
date_time_format, float_precision,
 formatter_for('OrderedDict')(format_value_map)
 formatter_for('OrderedMap')(format_value_map)
 formatter_for('OrderedMapSerializedKey')(format_value_map)
+formatter_for('map')(format_value_map)
 
 
-def format_value_utype(val, encoding, colormap, date_time_format, 
float_precision, nullval,
+def format_value_utype(val, cqltype, encoding, colormap, date_time_format, 
float_precision, nullval,
                        decimal_sep, thousands_sep, boolean_styles, **_):
-    def format_field_value(v):
+    def format_field_value(v, t):
         if v is None:
             return colorme(nullval, colormap, 'error')
-        return format_value(type(v), v, encoding=encoding, colormap=colormap,
+        return format_value(v, cqltype=t, encoding=encoding, colormap=colormap,
                             date_time_format=date_time_format, 
float_precision=float_precision,
                             nullval=nullval, quote=True, 
decimal_sep=decimal_sep,
                             thousands_sep=thousands_sep, 
boolean_styles=boolean_styles)
@@ -366,7 +468,8 @@ def format_value_utype(val, encoding, colormap, 
date_time_format, float_precisio
     def format_field_name(name):
         return format_value_text(name, encoding=encoding, colormap=colormap, 
quote=False)
 
-    subs = [(format_field_name(k), format_field_value(v)) for (k, v) in 
val._asdict().items()]
+    subs = [(format_field_name(k), format_field_value(v, t)) for ((k, v), t) 
in zip(val._asdict().items(),
+                                                                               
     cqltype.sub_types)]
     bval = '{' + ', '.join(get_str(k) + ': ' + get_str(v) for (k, v) in subs) 
+ '}'
     if colormap is NO_COLOR_MAP:
         return bval

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1de4a983/pylib/cqlshlib/tracing.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/tracing.py b/pylib/cqlshlib/tracing.py
index 2ded259..b886dba 100644
--- a/pylib/cqlshlib/tracing.py
+++ b/pylib/cqlshlib/tracing.py
@@ -16,6 +16,7 @@
 
 from cqlshlib.displaying import MAGENTA
 from datetime import datetime
+from formatting import CqlType
 import time
 from cassandra.query import QueryTrace, TraceUnavailable
 

Reply via email to