This is an automated email from the ASF dual-hosted git repository.

edimitrova pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 813e6ae  Revert "change six functions in cqlshlib to native Python 3 
ones"
813e6ae is described below

commit 813e6ae4a09b7172f3be276637ba7e29f6463405
Author: Ekaterina Dimitrova <ekaterina.dimitr...@datastax.com>
AuthorDate: Mon Mar 28 15:59:10 2022 -0400

    Revert "change six functions in cqlshlib to native Python 3 ones"
    
    This reverts commit 7275443eca804f5dde405f45811441dbc0c1f9fb.
---
 bin/cqlsh.py                       | 49 +++++++++++++++++----------
 pylib/cqlshlib/copyutil.py         | 69 ++++++++++++++++++++++----------------
 pylib/cqlshlib/displaying.py       |  2 +-
 pylib/cqlshlib/formatting.py       | 17 ++++++----
 pylib/cqlshlib/helptopics.py       |  2 +-
 pylib/cqlshlib/test/ansi_colors.py |  3 +-
 pylib/cqlshlib/test/run_cqlsh.py   |  2 +-
 pylib/cqlshlib/test/winpty.py      |  4 +--
 pylib/cqlshlib/tracing.py          |  4 ++-
 pylib/cqlshlib/util.py             |  2 +-
 pylib/requirements.txt             |  4 +++
 11 files changed, 97 insertions(+), 61 deletions(-)

diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index ad2a213..9e0e063 100755
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -110,8 +110,19 @@ if cql_zip:
     ver = os.path.splitext(os.path.basename(cql_zip))[0][len(CQL_LIB_PREFIX):]
     sys.path.insert(0, os.path.join(cql_zip, 'cassandra-driver-' + ver))
 
-import configparser
-from io import StringIO
+third_parties = ('futures-', 'six-')
+
+for lib in third_parties:
+    lib_zip = find_zip(lib)
+    if lib_zip:
+        sys.path.insert(0, lib_zip)
+
+# We cannot import six until we add its location to sys.path so the Python
+# interpreter can find it. Do not move this to the top.
+import six
+
+from six.moves import configparser, input
+from six import StringIO, ensure_text, ensure_str
 
 warnings.filterwarnings("ignore", r".*blist.*")
 try:
@@ -350,7 +361,7 @@ class DecodeError(Exception):
 
 
 def maybe_ensure_text(val):
-    return str(val) if val else val
+    return ensure_text(val) if val else val
 
 
 class FormatError(DecodeError):
@@ -415,7 +426,7 @@ def insert_driver_hooks():
 
 
 class Shell(cmd.Cmd):
-    custom_prompt = os.getenv('CQLSH_PROMPT', '')
+    custom_prompt = ensure_text(os.getenv('CQLSH_PROMPT', ''))
     if custom_prompt != '':
         custom_prompt += "\n"
     default_prompt = custom_prompt + "cqlsh> "
@@ -849,14 +860,15 @@ class Shell(cmd.Cmd):
 
     def get_input_line(self, prompt=''):
         if self.tty:
-            self.lastcmd = input(str(prompt))
-            line = self.lastcmd + '\n'
+            self.lastcmd = input(ensure_str(prompt))
+            line = ensure_text(self.lastcmd) + '\n'
         else:
-            self.lastcmd = self.stdin.readline()
+            self.lastcmd = ensure_text(self.stdin.readline())
             line = self.lastcmd
             if not len(line):
                 raise EOFError
         self.lineno += 1
+        line = ensure_text(line)
         return line
 
     def use_stdin_reader(self, until='', prompt=''):
@@ -917,6 +929,7 @@ class Shell(cmd.Cmd):
         Returns true if the statement is complete and was handled (meaning it
         can be reset).
         """
+        statementtext = ensure_text(statementtext)
         statementtext = self.strip_comment_blocks(statementtext)
         try:
             statements, endtoken_escaped = 
cqlruleset.cql_split_statements(statementtext)
@@ -962,7 +975,7 @@ class Shell(cmd.Cmd):
         if readline is not None:
             nl_count = srcstr.count("\n")
 
-            new_hist = srcstr.replace("\n", " ").rstrip()
+            new_hist = ensure_str(srcstr.replace("\n", " ").rstrip())
 
             if nl_count > 1 and self.last_hist != new_hist:
                 readline.add_history(new_hist)
@@ -1013,6 +1026,7 @@ class Shell(cmd.Cmd):
         self.tracing_enabled = tracing_was_enabled
 
     def perform_statement(self, statement):
+        statement = ensure_text(statement)
 
         stmt = SimpleStatement(statement, 
consistency_level=self.consistency_level, 
serial_consistency_level=self.serial_consistency_level, 
fetch_size=self.page_size if self.use_paging else None)
         success, future = self.perform_simple_statement(stmt)
@@ -1068,7 +1082,7 @@ class Shell(cmd.Cmd):
         try:
             result = future.result()
         except CQL_ERRORS as err:
-            err_msg = err.message if hasattr(err, 'message') else str(err)
+            err_msg = ensure_text(err.message if hasattr(err, 'message') else 
str(err))
             self.printerr(str(err.__class__.__name__) + ": " + err_msg)
         except Exception:
             import traceback
@@ -1383,7 +1397,7 @@ class Shell(cmd.Cmd):
                     self.describe_element(result)
 
             except CQL_ERRORS as err:
-                err_msg = err.message if hasattr(err, 'message') else str(err)
+                err_msg = ensure_text(err.message if hasattr(err, 'message') 
else str(err))
                 self.printerr(err_msg.partition("message=")[2].strip('"'))
             except Exception:
                 import traceback
@@ -1399,7 +1413,7 @@ class Shell(cmd.Cmd):
         """
         Print the output for a DESCRIBE KEYSPACES query
         """
-        names = [r['name'] for r in rows]
+        names = [ensure_str(r['name']) for r in rows]
 
         print('')
         cmd.Cmd.columnize(self, names)
@@ -1419,7 +1433,7 @@ class Shell(cmd.Cmd):
                 keyspace = row['keyspace_name']
                 names = list()
 
-            names.append(str(row['name']))
+            names.append(ensure_str(row['name']))
 
         if keyspace is not None:
             self.print_keyspace_element_names(keyspace, names)
@@ -1557,7 +1571,7 @@ class Shell(cmd.Cmd):
         if fname is not None:
             fname = self.cql_unprotect_value(fname)
 
-        copyoptnames = list(map(str.lower, parsed.get_binding('optnames', ())))
+        copyoptnames = list(map(six.text_type.lower, 
parsed.get_binding('optnames', ())))
         copyoptvals = list(map(self.cql_unprotect_value, 
parsed.get_binding('optvals', ())))
         opts = dict(list(zip(copyoptnames, copyoptvals)))
 
@@ -1978,10 +1992,11 @@ class Shell(cmd.Cmd):
             out = self.query_out
 
         # convert Exceptions, etc to text
-        if not isinstance(text, str):
-            text = str(text)
+        if not isinstance(text, six.text_type):
+            text = "{}".format(text)
 
         to_write = self.applycolor(text, color) + ('\n' if newline else '')
+        to_write = ensure_str(to_write)
         out.write(to_write)
 
     def flush_output(self):
@@ -2113,7 +2128,7 @@ def is_file_secure(filename):
 
 
 def read_options(cmdlineargs, environment):
-    configs = configparser.ConfigParser()
+    configs = configparser.SafeConfigParser() if sys.version_info < (3, 2) 
else configparser.ConfigParser()
     configs.read(CONFIG_FILE)
 
     rawconfigs = configparser.RawConfigParser()
@@ -2185,7 +2200,7 @@ def read_options(cmdlineargs, environment):
         options.credentials = ''  # ConfigParser.read() will ignore unreadable 
files
 
     if not options.username:
-        credentials = configparser.ConfigParser()
+        credentials = configparser.SafeConfigParser() if sys.version_info < 
(3, 2) else configparser.ConfigParser()
         credentials.read(options.credentials)
 
         # use the username from credentials file but fallback to cqlshrc if 
username is absent from the command line parameters
diff --git a/pylib/cqlshlib/copyutil.py b/pylib/cqlshlib/copyutil.py
index 92af3a3..b87340b 100644
--- a/pylib/cqlshlib/copyutil.py
+++ b/pylib/cqlshlib/copyutil.py
@@ -26,6 +26,7 @@ import platform
 import random
 import re
 import signal
+import six
 import struct
 import sys
 import threading
@@ -38,12 +39,15 @@ from calendar import timegm
 from collections import defaultdict, namedtuple
 from decimal import Decimal
 from random import randint
-from io import StringIO
+from io import BytesIO, StringIO
 from select import select
 from uuid import UUID
+from .util import profile_on, profile_off
 
-import configparser
-from queue import Queue
+from six import ensure_str, ensure_text
+from six.moves import configparser
+from six.moves import range
+from six.moves.queue import Queue
 
 from cassandra import OperationTimedOut
 from cassandra.cluster import Cluster, DefaultConnection
@@ -330,9 +334,9 @@ class CopyTask(object):
         opts = self.clean_options(self.maybe_read_config_file(opts, direction))
 
         dialect_options = dict()
-        dialect_options['quotechar'] = opts.pop('quote', '"')
-        dialect_options['escapechar'] = opts.pop('escape', '\\')
-        dialect_options['delimiter'] = opts.pop('delimiter', ',')
+        dialect_options['quotechar'] = ensure_str(opts.pop('quote', '"'))
+        dialect_options['escapechar'] = ensure_str(opts.pop('escape', '\\'))
+        dialect_options['delimiter'] = ensure_str(opts.pop('delimiter', ','))
         if dialect_options['quotechar'] == dialect_options['escapechar']:
             dialect_options['doublequote'] = True
             del dialect_options['escapechar']
@@ -340,7 +344,7 @@ class CopyTask(object):
             dialect_options['doublequote'] = False
 
         copy_options = dict()
-        copy_options['nullval'] = opts.pop('null', '')
+        copy_options['nullval'] = ensure_str(opts.pop('null', ''))
         copy_options['header'] = bool(opts.pop('header', '').lower() == 'true')
         copy_options['encoding'] = opts.pop('encoding', 'utf8')
         copy_options['maxrequests'] = int(opts.pop('maxrequests', 6))
@@ -362,7 +366,7 @@ class CopyTask(object):
         copy_options['consistencylevel'] = shell.consistency_level
         copy_options['decimalsep'] = opts.pop('decimalsep', '.')
         copy_options['thousandssep'] = opts.pop('thousandssep', '')
-        copy_options['boolstyle'] = [s.strip() for s in opts.pop('boolstyle', 
'True, False').split(',')]
+        copy_options['boolstyle'] = [ensure_str(s.strip()) for s in 
opts.pop('boolstyle', 'True, False').split(',')]
         copy_options['numprocesses'] = int(opts.pop('numprocesses', 
self.get_num_processes(16)))
         copy_options['begintoken'] = opts.pop('begintoken', '')
         copy_options['endtoken'] = opts.pop('endtoken', '')
@@ -565,7 +569,7 @@ class ExportWriter(object):
 
         if self.header:
             writer = csv.writer(self.current_dest.output, 
**self.options.dialect)
-            writer.writerow([str(c) for c in self.columns])
+            writer.writerow([ensure_str(c) for c in self.columns])
 
         return True
 
@@ -1724,7 +1728,7 @@ class ExportProcess(ChildProcess):
             return  # no rows in this range
 
         try:
-            output = StringIO()
+            output = StringIO() if six.PY3 else BytesIO()
             writer = csv.writer(output, **self.options.dialect)
 
             for row in rows:
@@ -1754,7 +1758,7 @@ class ExportProcess(ChildProcess):
                               float_precision=cqltype.precision, 
nullval=self.nullval, quote=False,
                               decimal_sep=self.decimal_sep, 
thousands_sep=self.thousands_sep,
                               boolean_styles=self.boolean_styles)
-        return formatted
+        return formatted if six.PY3 else formatted.encode('utf8')
 
     def close(self):
         ChildProcess.close(self)
@@ -1894,7 +1898,7 @@ class ImportConversion(object):
         select_query = 'SELECT * FROM %s.%s WHERE %s' % 
(protect_name(parent.ks),
                                                          
protect_name(parent.table),
                                                          where_clause)
-        return parent.session.prepare(select_query)
+        return parent.session.prepare(ensure_str(select_query))
 
     @staticmethod
     def unprotect(v):
@@ -1930,20 +1934,20 @@ class ImportConversion(object):
                 return BlobType(v[2:].decode("hex"))
 
         def convert_text(v, **_):
-            return str(v)
+            return ensure_str(v)
 
         def convert_uuid(v, **_):
             return UUID(v)
 
         def convert_bool(v, **_):
-            return True if v.lower() == self.boolean_styles[0].lower() else 
False
+            return True if v.lower() == 
ensure_str(self.boolean_styles[0]).lower() else False
 
         def get_convert_integer_fcn(adapter=int):
             """
             Return a slow and a fast integer conversion function depending on 
self.thousands_sep
             """
             if self.thousands_sep:
-                return lambda v, ct=cql_type: 
adapter(v.replace(self.thousands_sep, ''))
+                return lambda v, ct=cql_type: 
adapter(v.replace(self.thousands_sep, ensure_str('')))
             else:
                 return lambda v, ct=cql_type: adapter(v)
 
@@ -1951,8 +1955,8 @@ class ImportConversion(object):
             """
             Return a slow and a fast decimal conversion function depending on 
self.thousands_sep and self.decimal_sep
             """
-            empty_str = ''
-            dot_str = '.'
+            empty_str = ensure_str('')
+            dot_str = ensure_str('.')
             if self.thousands_sep and self.decimal_sep:
                 return lambda v, ct=cql_type: 
adapter(v.replace(self.thousands_sep, empty_str).replace(self.decimal_sep, 
dot_str))
             elif self.thousands_sep:
@@ -2016,8 +2020,14 @@ class ImportConversion(object):
 
         def convert_datetime(val, **_):
             try:
-                dtval = datetime.datetime.strptime(val, self.date_time_format)
-                return dtval.timestamp() * 1000
+                if six.PY2:
+                    # Python 2 implementation
+                    tval = time.strptime(val, self.date_time_format)
+                    return timegm(tval) * 1e3  # scale seconds to millis for 
the raw value
+                else:
+                    # Python 3 implementation
+                    dtval = datetime.datetime.strptime(val, 
self.date_time_format)
+                    return dtval.timestamp() * 1000
             except ValueError:
                 pass  # if it's not in the default format we try CQL formats
 
@@ -2068,8 +2078,8 @@ class ImportConversion(object):
             """
             See ImmutableDict above for a discussion of why a special object 
is needed here.
             """
-            split_format_str = '{%s}'
-            sep = ':'
+            split_format_str = ensure_str('{%s}')
+            sep = ensure_str(':')
             return ImmutableDict(frozenset((convert_mandatory(ct.subtypes[0], 
v[0]), convert(ct.subtypes[1], v[1]))
                                  for v in [split(split_format_str % vv, 
sep=sep) for vv in split(val)]))
 
@@ -2082,8 +2092,8 @@ class ImportConversion(object):
             Also note that it is possible that the subfield names in the csv 
are in the
             wrong order, so we must sort them according to ct.fieldnames, see 
CASSANDRA-12959.
             """
-            split_format_str = '{%s}'
-            sep = ':'
+            split_format_str = ensure_str('{%s}')
+            sep = ensure_str(':')
             vals = [v for v in [split(split_format_str % vv, sep=sep) for vv 
in split(val)]]
             dict_vals = dict((unprotect(v[0]), v[1]) for v in vals)
             sorted_converted_vals = [(n, convert(t, dict_vals[n]) if n in 
dict_vals else self.get_null_val())
@@ -2141,7 +2151,7 @@ class ImportConversion(object):
         or "NULL" otherwise. Note that for counters we never use prepared 
statements, so we
         only check is_counter when use_prepared_statements is false.
         """
-        return None if self.use_prepared_statements else ("0" if 
self.is_counter else "NULL")
+        return None if self.use_prepared_statements else (ensure_str("0") if 
self.is_counter else ensure_str("NULL"))
 
     def convert_row(self, row):
         """
@@ -2426,6 +2436,7 @@ class ImportProcess(ChildProcess):
             if self.ttl >= 0:
                 query += 'USING TTL %s' % (self.ttl,)
             make_statement = 
self.wrap_make_statement(self.make_non_prepared_batch_statement)
+            query = ensure_str(query)
 
         conv = ImportConversion(self, table_meta, prepared_statement)
         tm = TokenMap(self.ks, self.hostname, self.local_dc, self.session)
@@ -2493,12 +2504,12 @@ class ImportProcess(ChildProcess):
             set_clause = []
             for i, value in enumerate(row):
                 if i in conv.primary_key_indexes:
-                    where_clause.append("{}={}".format(self.valid_columns[i], 
str(value)))
+                    
where_clause.append(ensure_text("{}={}").format(self.valid_columns[i], 
ensure_text(value)))
                 else:
-                    set_clause.append("{}={}+{}".format(self.valid_columns[i], 
self.valid_columns[i], str(value)))
+                    
set_clause.append(ensure_text("{}={}+{}").format(self.valid_columns[i], 
self.valid_columns[i], ensure_text(value)))
 
-            full_query_text = query % (','.join(set_clause), ' AND 
'.join(where_clause))
-            statement.add(full_query_text)
+            full_query_text = query % (ensure_text(',').join(set_clause), 
ensure_text(' AND ').join(where_clause))
+            statement.add(ensure_str(full_query_text))
         return statement
 
     def make_prepared_batch_statement(self, query, _, batch, replicas):
@@ -2522,7 +2533,7 @@ class ImportProcess(ChildProcess):
         statement = BatchStatement(batch_type=BatchType.UNLOGGED, 
consistency_level=self.consistency_level)
         statement.replicas = replicas
         statement.keyspace = self.ks
-        field_sep = ','
+        field_sep = b',' if six.PY2 else ','
         statement._statements_and_parameters = [(False, query % 
(field_sep.join(r),), ()) for r in batch['rows']]
         return statement
 
diff --git a/pylib/cqlshlib/displaying.py b/pylib/cqlshlib/displaying.py
index 424d633..7109f11 100644
--- a/pylib/cqlshlib/displaying.py
+++ b/pylib/cqlshlib/displaying.py
@@ -41,7 +41,7 @@ def get_str(val):
     return val
 
 
-class FormattedValue:
+class FormattedValue(object):
 
     def __init__(self, strval, coloredval=None, displaywidth=None):
         self.strval = strval
diff --git a/pylib/cqlshlib/formatting.py b/pylib/cqlshlib/formatting.py
index 42c9305..b82486a 100644
--- a/pylib/cqlshlib/formatting.py
+++ b/pylib/cqlshlib/formatting.py
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import binascii
 import calendar
 import datetime
 import math
@@ -21,6 +22,8 @@ import os
 import re
 import sys
 
+from six import ensure_text
+
 from collections import defaultdict
 
 from cassandra.cqltypes import EMPTY
@@ -118,7 +121,7 @@ class DateTimeFormat:
         self.milliseconds_only = milliseconds_only  # the microseconds part, 
.NNNNNN, wil be rounded to .NNN
 
 
-class CqlType:
+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.
@@ -200,7 +203,7 @@ class CqlType:
 
 
 def format_value_default(val, colormap, **_):
-    val = str(val)
+    val = ensure_text(str(val))
     escapedval = val.replace('\\', '\\\\')
     bval = controlchars_re.sub(_show_control_chars, escapedval)
     return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap)
@@ -232,7 +235,7 @@ def formatter_for(typname):
     return registrator
 
 
-class BlobType:
+class BlobType(object):
     def __init__(self, val):
         self.val = val
 
@@ -242,7 +245,7 @@ class BlobType:
 
 @formatter_for('BlobType')
 def format_value_blob(val, colormap, **_):
-    bval = '0x' + val.hex()
+    bval = ensure_text('0x') + ensure_text(binascii.hexlify(val))
     return colorme(bval, colormap, 'blob')
 
 
@@ -252,7 +255,7 @@ formatter_for('blob')(format_value_blob)
 
 
 def format_python_formatted_type(val, colormap, color, quote=False):
-    bval = str(val)
+    bval = ensure_text(str(val))
     if quote:
         bval = "'%s'" % bval
     return colorme(bval, colormap, color)
@@ -322,7 +325,7 @@ formatter_for('double')(format_floating_point_type)
 def format_integer_type(val, colormap, thousands_sep=None, **_):
     # base-10 only for now; support others?
     bval = format_integer_with_thousands_sep(val, thousands_sep) if 
thousands_sep else str(val)
-    bval = str(bval)
+    bval = ensure_text(bval)
     return colorme(bval, colormap, 'int')
 
 
@@ -357,7 +360,7 @@ def format_value_timestamp(val, colormap, date_time_format, 
quote=False, **_):
         if date_time_format.milliseconds_only:
             bval = round_microseconds(bval)
     else:
-        bval = str(val)
+        bval = ensure_text(str(val))
 
     if quote:
         bval = "'%s'" % bval
diff --git a/pylib/cqlshlib/helptopics.py b/pylib/cqlshlib/helptopics.py
index 9be56b9..46cd156 100644
--- a/pylib/cqlshlib/helptopics.py
+++ b/pylib/cqlshlib/helptopics.py
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 
-class CQL3HelpTopics:
+class CQL3HelpTopics(object):
     def get_help_topics(self):
         return [t[5:] for t in dir(self) if t.startswith('help_')]
 
diff --git a/pylib/cqlshlib/test/ansi_colors.py 
b/pylib/cqlshlib/test/ansi_colors.py
index 494b7c6..2bdc736 100644
--- a/pylib/cqlshlib/test/ansi_colors.py
+++ b/pylib/cqlshlib/test/ansi_colors.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 
 import re
+import six
 
 LIGHT = 0o10
 
@@ -105,7 +106,7 @@ class ColoredChar(object):
 
 class ColoredText(object):
     def __init__(self, source=''):
-        if isinstance(source, str):
+        if isinstance(source, six.text_type):
             plain, colors = self.parse_ansi_colors(source)
             self.chars = list(map(ColoredChar, plain, colors))
         else:
diff --git a/pylib/cqlshlib/test/run_cqlsh.py b/pylib/cqlshlib/test/run_cqlsh.py
index 180796a..9adb854 100644
--- a/pylib/cqlshlib/test/run_cqlsh.py
+++ b/pylib/cqlshlib/test/run_cqlsh.py
@@ -30,7 +30,7 @@ from os.path import join
 import pty
 DEFAULT_PREFIX = os.linesep
 
-DEFAULT_CQLSH_PROMPT = DEFAULT_PREFIX + r'(\S+@)?cqlsh(:\S+)?> '
+DEFAULT_CQLSH_PROMPT = DEFAULT_PREFIX + '(\S+@)?cqlsh(:\S+)?> '
 DEFAULT_CQLSH_TERM = 'xterm'
 
 try:
diff --git a/pylib/cqlshlib/test/winpty.py b/pylib/cqlshlib/test/winpty.py
index 02b1981..9381a2f 100644
--- a/pylib/cqlshlib/test/winpty.py
+++ b/pylib/cqlshlib/test/winpty.py
@@ -15,8 +15,8 @@
 # limitations under the License.
 
 from threading import Thread
-from io import StringIO
-from queue import Queue, Empty
+from six import StringIO
+from six.moves.queue import Queue, Empty
 
 
 class WinPty(object):
diff --git a/pylib/cqlshlib/tracing.py b/pylib/cqlshlib/tracing.py
index 3db8e34..e068250 100644
--- a/pylib/cqlshlib/tracing.py
+++ b/pylib/cqlshlib/tracing.py
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from datetime import datetime
+from datetime import datetime, timedelta
 import time
 
 from cassandra.query import QueryTrace, TraceUnavailable
@@ -73,6 +73,8 @@ def make_trace_rows(trace):
     if trace.duration:
         finished_at = (datetime_from_utc_to_local(trace.started_at) + 
trace.duration)
         rows.append(['Request complete', str(finished_at), trace.coordinator, 
total_micro_seconds(trace.duration), trace.client])
+    else:
+        finished_at = trace.duration = "--"
 
     return rows
 
diff --git a/pylib/cqlshlib/util.py b/pylib/cqlshlib/util.py
index f29141d..82a332f 100644
--- a/pylib/cqlshlib/util.py
+++ b/pylib/cqlshlib/util.py
@@ -21,7 +21,7 @@ import pstats
 
 
 from datetime import timedelta, tzinfo
-from io import StringIO
+from six import StringIO
 
 try:
     from line_profiler import LineProfiler
diff --git a/pylib/requirements.txt b/pylib/requirements.txt
index 4d66ef2..10084dd 100644
--- a/pylib/requirements.txt
+++ b/pylib/requirements.txt
@@ -1,3 +1,7 @@
+# See python driver docs: six have to be installed before
+# cythonizing the driver, perhaps only on old pips.
+# 
http://datastax.github.io/python-driver/installation.html#cython-based-extensions
+six>=1.12.0
 -e 
git+https://github.com/datastax/python-driver.git@cassandra-test#egg=cassandra-driver
 # Used ccm version is tracked by cassandra-test branch in ccm repo. Please 
create a PR there for fixes or upgrades to new releases.
 -e git+https://github.com/riptano/ccm.git@cassandra-test#egg=ccm

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to