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

smiklosovic 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 ecceb446e0 Refactor internals of cqlsh.py to cqlshlib
ecceb446e0 is described below

commit ecceb446e00ea9e567ba45f1b422cb04862ef044
Author: Brad Schoening <5796692+bschoen...@users.noreply.github.com>
AuthorDate: Wed Sep 14 14:28:10 2022 +0300

    Refactor internals of cqlsh.py to cqlshlib
    
    patch by Brad Schoening; reviewed by Stefan Miklosovic and Brandon Williams 
for CASSANDRA-17531
---
 bin/cqlsh.py                                | 2321 +--------------------------
 bin/cqlsh.py => pylib/cqlshlib/cqlshmain.py |  170 +-
 2 files changed, 61 insertions(+), 2430 deletions(-)

diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index 9a561eb200..7e6db9bdb8 100755
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -16,26 +16,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import cmd
-import codecs
-import configparser
-import csv
-import errno
-import getpass
-import optparse
 import os
 import platform
-import re
-import stat
-import subprocess
 import sys
-import traceback
-import warnings
-import webbrowser
-from contextlib import contextmanager
 from glob import glob
-from io import StringIO
-from uuid import UUID
 
 if sys.version_info < (3, 6):
     sys.exit("\ncqlsh requires Python 3.6+\n")
@@ -44,50 +28,9 @@ if sys.version_info < (3, 6):
 if platform.python_implementation().startswith('Jython'):
     sys.exit("\nCQL Shell does not run on Jython\n")
 
-UTF8 = 'utf-8'
-
-description = "CQL Shell for Apache Cassandra"
-version = "6.2.0"
-
-readline = None
-try:
-    # check if tty first, cause readline doesn't check, and only cares
-    # about $TERM. we don't want the funky escape code stuff to be
-    # output if not a tty.
-    if sys.stdin.isatty():
-        import readline
-except ImportError:
-    pass
-
 CQL_LIB_PREFIX = 'cassandra-driver-internal-only-'
 
 CASSANDRA_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 
'..')
-CASSANDRA_CQL_HTML_FALLBACK = 
'https://cassandra.apache.org/doc/latest/cql/index.html'
-
-# default location of local CQL.html
-if os.path.exists(CASSANDRA_PATH + '/doc/cql3/CQL.html'):
-    # default location of local CQL.html
-    CASSANDRA_CQL_HTML = 'file://' + CASSANDRA_PATH + '/doc/cql3/CQL.html'
-elif os.path.exists('/usr/share/doc/cassandra/CQL.html'):
-    # fallback to package file
-    CASSANDRA_CQL_HTML = 'file:///usr/share/doc/cassandra/CQL.html'
-else:
-    # fallback to online version
-    CASSANDRA_CQL_HTML = CASSANDRA_CQL_HTML_FALLBACK
-
-# On Linux, the Python webbrowser module uses the 'xdg-open' executable
-# to open a file/URL. But that only works, if the current session has been
-# opened from _within_ a desktop environment. I.e. 'xdg-open' will fail,
-# if the session's been opened via ssh to a remote box.
-#
-try:
-    webbrowser.register_standard_browsers()  # registration is otherwise lazy 
in Python3
-except AttributeError:
-    pass
-if webbrowser._tryorder and webbrowser._tryorder[0] == 'xdg-open' and 
os.environ.get('XDG_DATA_DIRS', '') == '':
-    # only on Linux (some OS with xdg-open)
-    webbrowser._tryorder.remove('xdg-open')
-    webbrowser._tryorder.append('xdg-open')
 
 # use bundled lib for python-cql if available. if there
 # is a ../lib dir, use bundled libs there preferentially.
@@ -120,7 +63,6 @@ for lib in third_parties:
     if lib_zip:
         sys.path.insert(0, lib_zip)
 
-warnings.filterwarnings("ignore", r".*blist.*")
 try:
     import cassandra
 except ImportError as e:
@@ -130,14 +72,6 @@ except ImportError as e:
              'Module load path: %r\n\n'
              'Error: %s\n' % (sys.executable, sys.path, e))
 
-from cassandra.auth import PlainTextAuthProvider
-from cassandra.cluster import Cluster
-from cassandra.cqltypes import cql_typename
-from cassandra.marshal import int64_unpack
-from cassandra.metadata import (ColumnMetadata, KeyspaceMetadata, 
TableMetadata)
-from cassandra.policies import WhiteListRoundRobinPolicy
-from cassandra.query import SimpleStatement, ordered_dict_factory, 
TraceUnavailable
-from cassandra.util import datetime_from_timestamp
 
 # cqlsh should run correctly when run out of a Cassandra source tree,
 # out of an unpacked Cassandra tarball, and after a proper package install.
@@ -145,2261 +79,10 @@ cqlshlibdir = os.path.join(CASSANDRA_PATH, 'pylib')
 if os.path.isdir(cqlshlibdir):
     sys.path.insert(0, cqlshlibdir)
 
-from cqlshlib import cql3handling, pylexotron, sslhandling, cqlshhandling, 
authproviderhandling
-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, CqlType, 
DateTimeFormat,
-                                 format_by_type)
-from cqlshlib.tracing import print_trace, print_trace_session
-from cqlshlib.util import get_file_encoding_bomsize
-from cqlshlib.util import is_file_secure
-
-
-DEFAULT_HOST = '127.0.0.1'
-DEFAULT_PORT = 9042
-DEFAULT_SSL = False
-DEFAULT_CONNECT_TIMEOUT_SECONDS = 5
-DEFAULT_REQUEST_TIMEOUT_SECONDS = 10
-
-DEFAULT_FLOAT_PRECISION = 5
-DEFAULT_DOUBLE_PRECISION = 5
-DEFAULT_MAX_TRACE_WAIT = 10
-
-if readline is not None and readline.__doc__ is not None and 'libedit' in 
readline.__doc__:
-    DEFAULT_COMPLETEKEY = '\t'
-else:
-    DEFAULT_COMPLETEKEY = 'tab'
-
-cqldocs = None
-cqlruleset = None
-
-epilog = """Connects to %(DEFAULT_HOST)s:%(DEFAULT_PORT)d by default. These
-defaults can be changed by setting $CQLSH_HOST and/or $CQLSH_PORT. When a
-host (and optional port number) are given on the command line, they take
-precedence over any defaults.""" % globals()
-
-parser = optparse.OptionParser(description=description, epilog=epilog,
-                               usage="Usage: %prog [options] [host [port]]",
-                               version='cqlsh ' + version)
-parser.add_option("-C", "--color", action='store_true', dest='color',
-                  help='Always use color output')
-parser.add_option("--no-color", action='store_false', dest='color',
-                  help='Never use color output')
-parser.add_option("--browser", dest='browser', help="""The browser to use to 
display CQL help, where BROWSER can be:
-                                                    - one of the supported 
browsers in https://docs.python.org/3/library/webbrowser.html.
-                                                    - browser path followed by 
%s, example: /usr/bin/google-chrome-stable %s""")
-parser.add_option('--ssl', action='store_true', help='Use SSL', default=False)
-parser.add_option("-u", "--username", help="Authenticate as user.")
-parser.add_option("-p", "--password", help="Authenticate using password.")
-parser.add_option('-k', '--keyspace', help='Authenticate to the given 
keyspace.')
-parser.add_option("-f", "--file", help="Execute commands from FILE, then exit")
-parser.add_option('--debug', action='store_true',
-                  help='Show additional debugging information')
-parser.add_option('--coverage', action='store_true',
-                  help='Collect coverage data')
-parser.add_option("--encoding", help="Specify a non-default encoding for 
output."
-                  + " (Default: %s)" % (UTF8,))
-parser.add_option("--cqlshrc", help="Specify an alternative cqlshrc file 
location.")
-parser.add_option("--credentials", help="Specify an alternative credentials 
file location.")
-parser.add_option('--cqlversion', default=None,
-                  help='Specify a particular CQL version, '
-                       'by default the highest version supported by the server 
will be used.'
-                       ' Examples: "3.0.3", "3.1.0"')
-parser.add_option("--protocol-version", type="int", default=None,
-                  help='Specify a specific protcol version otherwise the 
client will default and downgrade as necessary')
-
-parser.add_option("-e", "--execute", help='Execute the statement and quit.')
-parser.add_option("--connect-timeout", 
default=DEFAULT_CONNECT_TIMEOUT_SECONDS, dest='connect_timeout',
-                  help='Specify the connection timeout in seconds (default: 
%default seconds).')
-parser.add_option("--request-timeout", 
default=DEFAULT_REQUEST_TIMEOUT_SECONDS, dest='request_timeout',
-                  help='Specify the default request timeout in seconds 
(default: %default seconds).')
-parser.add_option("-t", "--tty", action='store_true', dest='tty',
-                  help='Force tty mode (command prompt).')
-parser.add_option('-v', action="version", help='Print the current version of 
cqlsh.')
-
-# This is a hidden option to suppress the warning when the -p/--password 
command line option is used.
-# Power users may use this option if they know no other people has access to 
the system where cqlsh is run or don't care about security.
-# Use of this option in scripting is discouraged. Please use a (temporary) 
credentials file where possible.
-# The Cassandra distributed tests (dtests) also use this option in some tests 
when a well-known password is supplied via the command line.
-parser.add_option("--insecure-password-without-warning", action='store_true', 
dest='insecure_password_without_warning',
-                  help=optparse.SUPPRESS_HELP)
-
-opt_values = optparse.Values()
-(options, arguments) = parser.parse_args(sys.argv[1:], values=opt_values)
-
-# BEGIN history/config definition
-
-
-def mkdirp(path):
-    """Creates all parent directories up to path parameter or fails when path 
exists, but it is not a directory."""
-
-    try:
-        os.makedirs(path)
-    except OSError:
-        if not os.path.isdir(path):
-            raise
-
-
-def resolve_cql_history_file():
-    default_cql_history = os.path.expanduser(os.path.join('~', '.cassandra', 
'cqlsh_history'))
-    if 'CQL_HISTORY' in os.environ:
-        return os.environ['CQL_HISTORY']
-    else:
-        return default_cql_history
-
-
-HISTORY = resolve_cql_history_file()
-HISTORY_DIR = os.path.dirname(HISTORY)
-
-try:
-    mkdirp(HISTORY_DIR)
-except OSError:
-    print('\nWarning: Cannot create directory at `%s`. Command history will 
not be saved. Please check what was the environment property CQL_HISTORY set 
to.\n' % HISTORY_DIR)
-
-DEFAULT_CQLSHRC = os.path.expanduser(os.path.join('~', '.cassandra', 
'cqlshrc'))
-
-if hasattr(options, 'cqlshrc'):
-    CONFIG_FILE = os.path.expanduser(options.cqlshrc)
-    if not os.path.exists(CONFIG_FILE):
-        print('\nWarning: Specified cqlshrc location `%s` does not exist.  
Using `%s` instead.\n' % (CONFIG_FILE, DEFAULT_CQLSHRC))
-        CONFIG_FILE = DEFAULT_CQLSHRC
-else:
-    CONFIG_FILE = DEFAULT_CQLSHRC
-
-CQL_DIR = os.path.dirname(CONFIG_FILE)
-
-CQL_ERRORS = (
-    cassandra.AlreadyExists, cassandra.AuthenticationFailed, 
cassandra.CoordinationFailure,
-    cassandra.InvalidRequest, cassandra.Timeout, cassandra.Unauthorized, 
cassandra.OperationTimedOut,
-    cassandra.cluster.NoHostAvailable,
-    cassandra.connection.ConnectionBusy, cassandra.connection.ProtocolError, 
cassandra.connection.ConnectionException,
-    cassandra.protocol.ErrorMessage, cassandra.protocol.InternalError, 
cassandra.query.TraceUnavailable
-)
-
-debug_completion = bool(os.environ.get('CQLSH_DEBUG_COMPLETION', '') == 'YES')
-
-
-class NoKeyspaceError(Exception):
-    pass
-
-
-class KeyspaceNotFound(Exception):
-    pass
-
-
-class ColumnFamilyNotFound(Exception):
-    pass
-
-
-class IndexNotFound(Exception):
-    pass
-
-
-class MaterializedViewNotFound(Exception):
-    pass
-
-
-class ObjectNotFound(Exception):
-    pass
-
-
-class VersionNotSupported(Exception):
-    pass
-
-
-class UserTypeNotFound(Exception):
-    pass
-
-
-class FunctionNotFound(Exception):
-    pass
-
-
-class AggregateNotFound(Exception):
-    pass
-
-
-class DecodeError(Exception):
-    verb = 'decode'
-
-    def __init__(self, thebytes, err, colname=None):
-        self.thebytes = thebytes
-        self.err = err
-        self.colname = colname
-
-    def __str__(self):
-        return str(self.thebytes)
-
-    def message(self):
-        what = 'value %r' % (self.thebytes,)
-        if self.colname is not None:
-            what = 'value %r (for column %r)' % (self.thebytes, self.colname)
-        return 'Failed to %s %s : %s' \
-               % (self.verb, what, self.err)
-
-    def __repr__(self):
-        return '<%s %s>' % (self.__class__.__name__, self.message())
-
-
-def maybe_ensure_text(val):
-    return str(val) if val else val
-
-
-class FormatError(DecodeError):
-    verb = 'format'
-
-
-def full_cql_version(ver):
-    while ver.count('.') < 2:
-        ver += '.0'
-    ver_parts = ver.split('-', 1) + ['']
-    vertuple = tuple(list(map(int, ver_parts[0].split('.'))) + [ver_parts[1]])
-    return ver, vertuple
-
-
-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(val, cqltype=cqltype, encoding=encoding, 
colormap=colormap,
-                          addcolor=addcolor, nullval=nullval, 
date_time_format=date_time_format,
-                          float_precision=float_precision)
-
-
-def show_warning_without_quoting_line(message, category, filename, lineno, 
file=None, line=None):
-    if file is None:
-        file = sys.stderr
-    try:
-        file.write(warnings.formatwarning(message, category, filename, lineno, 
line=''))
-    except IOError:
-        pass
-
-
-warnings.showwarning = show_warning_without_quoting_line
-warnings.filterwarnings('always', 
category=cql3handling.UnexpectedTableStructure)
-
-
-def insert_driver_hooks():
-
-    class DateOverFlowWarning(RuntimeWarning):
-        pass
-
-    # Native datetime types blow up outside of datetime.[MIN|MAX]_YEAR. We 
will fall back to an int timestamp
-    def deserialize_date_fallback_int(byts, protocol_version):
-        timestamp_ms = int64_unpack(byts)
-        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."))
-            return timestamp_ms
-
-    cassandra.cqltypes.DateType.deserialize = 
staticmethod(deserialize_date_fallback_int)
-
-    if hasattr(cassandra, 'deserializers'):
-        del cassandra.deserializers.DesDateType
-
-    # Return cassandra.cqltypes.EMPTY instead of None for empty values
-    cassandra.cqltypes.CassandraType.support_empty_values = True
-
-
-class Shell(cmd.Cmd):
-    custom_prompt = os.getenv('CQLSH_PROMPT', '')
-    if custom_prompt != '':
-        custom_prompt += "\n"
-    default_prompt = custom_prompt + "cqlsh> "
-    continue_prompt = "   ... "
-    keyspace_prompt = custom_prompt + "cqlsh:{}> "
-    keyspace_continue_prompt = "{}    ... "
-    show_line_nums = False
-    debug = False
-    coverage = False
-    coveragerc_path = None
-    stop = False
-    last_hist = None
-    shunted_query_out = None
-    use_paging = True
-
-    default_page_size = 100
-
-    def __init__(self, hostname, port, color=False,
-                 username=None, encoding=None, stdin=None, tty=True,
-                 completekey=DEFAULT_COMPLETEKEY, browser=None, use_conn=None,
-                 cqlver=None, keyspace=None,
-                 tracing_enabled=False, expand_enabled=False,
-                 display_nanotime_format=DEFAULT_NANOTIME_FORMAT,
-                 display_timestamp_format=DEFAULT_TIMESTAMP_FORMAT,
-                 display_date_format=DEFAULT_DATE_FORMAT,
-                 display_float_precision=DEFAULT_FLOAT_PRECISION,
-                 display_double_precision=DEFAULT_DOUBLE_PRECISION,
-                 display_timezone=None,
-                 max_trace_wait=DEFAULT_MAX_TRACE_WAIT,
-                 ssl=False,
-                 single_statement=None,
-                 request_timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS,
-                 protocol_version=None,
-                 connect_timeout=DEFAULT_CONNECT_TIMEOUT_SECONDS,
-                 is_subshell=False,
-                 auth_provider=None):
-        cmd.Cmd.__init__(self, completekey=completekey)
-        self.hostname = hostname
-        self.port = port
-        self.auth_provider = auth_provider
-        self.username = username
-
-        if isinstance(auth_provider, PlainTextAuthProvider):
-            self.username = auth_provider.username
-            if not auth_provider.password:
-                # if no password is provided, we need to query the user to get 
one.
-                password = getpass.getpass()
-                self.auth_provider = 
PlainTextAuthProvider(username=auth_provider.username, password=password)
-
-        self.keyspace = keyspace
-        self.ssl = ssl
-        self.tracing_enabled = tracing_enabled
-        self.page_size = self.default_page_size
-        self.expand_enabled = expand_enabled
-        if use_conn:
-            self.conn = use_conn
-        else:
-            kwargs = {}
-            if protocol_version is not None:
-                kwargs['protocol_version'] = protocol_version
-            self.conn = Cluster(contact_points=(self.hostname,), 
port=self.port, cql_version=cqlver,
-                                auth_provider=self.auth_provider,
-                                ssl_options=sslhandling.ssl_settings(hostname, 
CONFIG_FILE) if ssl else None,
-                                
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
-                                control_connection_timeout=connect_timeout,
-                                connect_timeout=connect_timeout,
-                                **kwargs)
-        self.owns_connection = not use_conn
-
-        if keyspace:
-            self.session = self.conn.connect(keyspace)
-        else:
-            self.session = self.conn.connect()
-
-        if browser == "":
-            browser = None
-        self.browser = browser
-        self.color = color
-
-        self.display_nanotime_format = display_nanotime_format
-        self.display_timestamp_format = display_timestamp_format
-        self.display_date_format = display_date_format
-
-        self.display_float_precision = display_float_precision
-        self.display_double_precision = display_double_precision
-
-        self.display_timezone = display_timezone
-
-        self.session.default_timeout = request_timeout
-        self.session.row_factory = ordered_dict_factory
-        self.session.default_consistency_level = cassandra.ConsistencyLevel.ONE
-        self.get_connection_versions()
-        self.set_expanded_cql_version(self.connection_versions['cql'])
-
-        self.current_keyspace = keyspace
-
-        self.max_trace_wait = max_trace_wait
-        self.session.max_trace_wait = max_trace_wait
-
-        self.tty = tty
-        self.encoding = encoding
-
-        self.output_codec = codecs.lookup(encoding)
-
-        self.statement = StringIO()
-        self.lineno = 1
-        self.in_comment = False
-
-        self.prompt = ''
-        if stdin is None:
-            stdin = sys.stdin
-
-        if tty:
-            self.reset_prompt()
-            self.report_connection()
-            print('Use HELP for help.')
-        else:
-            self.show_line_nums = True
-        self.stdin = stdin
-        self.query_out = sys.stdout
-        self.consistency_level = cassandra.ConsistencyLevel.ONE
-        self.serial_consistency_level = cassandra.ConsistencyLevel.SERIAL
-
-        self.empty_lines = 0
-        self.statement_error = False
-        self.single_statement = single_statement
-        self.is_subshell = is_subshell
-
-    @property
-    def batch_mode(self):
-        return not self.tty
-
-    def set_expanded_cql_version(self, ver):
-        ver, vertuple = full_cql_version(ver)
-        self.cql_version = ver
-        self.cql_ver_tuple = vertuple
-
-    def cqlver_atleast(self, major, minor=0, patch=0):
-        return self.cql_ver_tuple[:3] >= (major, minor, patch)
-
-    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)
-            precision = self.display_double_precision if cqltype is not None 
and cqltype.type_name == 'double' \
-                else self.display_float_precision
-            return format_value(val, cqltype=cqltype, 
encoding=self.output_codec.name,
-                                addcolor=self.color, 
date_time_format=dtformats,
-                                float_precision=precision, **kwargs)
-        except Exception as e:
-            err = FormatError(val, e)
-            self.decoding_errors.append(err)
-            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()
-        # check column role and color appropriately
-        if table_meta:
-            if name in [col.name for col in table_meta.partition_key]:
-                column_colors.default_factory = lambda: RED
-            elif name in [col.name for col in table_meta.clustering_key]:
-                column_colors.default_factory = lambda: CYAN
-            elif name in table_meta.columns and 
table_meta.columns[name].is_static:
-                column_colors.default_factory = lambda: WHITE
-        return self.myformat_value(name, colormap=column_colors)
-
-    def report_connection(self):
-        self.show_host()
-        self.show_version()
-
-    def show_host(self):
-        print("Connected to {0} at {1}:{2}"
-              .format(self.applycolor(self.get_cluster_name(), BLUE),
-                      self.hostname,
-                      self.port))
-
-    def show_version(self):
-        vers = self.connection_versions.copy()
-        vers['shver'] = version
-        # system.Versions['cql'] apparently does not reflect changes with
-        # set_cql_version.
-        vers['cql'] = self.cql_version
-        print("[cqlsh %(shver)s | Cassandra %(build)s | CQL spec %(cql)s | 
Native protocol v%(protocol)s]" % vers)
-
-    def show_session(self, sessionid, partial_session=False):
-        print_trace_session(self, self.session, sessionid, partial_session)
-
-    def show_replicas(self, token_value, keyspace=None):
-        ks = self.current_keyspace if keyspace is None else keyspace
-        token_map = self.conn.metadata.token_map
-        nodes = token_map.get_replicas(ks, token_map.token_class(token_value))
-        addresses = [x.address for x in nodes]
-        print(f"{addresses}")
-
-    def get_connection_versions(self):
-        result, = self.session.execute("select * from system.local where key = 
'local'")
-        vers = {
-            'build': result['release_version'],
-            'protocol': self.conn.protocol_version,
-            'cql': result['cql_version'],
-        }
-        self.connection_versions = vers
-
-    def get_keyspace_names(self):
-        return list(self.conn.metadata.keyspaces)
-
-    def get_columnfamily_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return list(self.get_keyspace_meta(ksname).tables)
-
-    def get_materialized_view_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return list(self.get_keyspace_meta(ksname).views)
-
-    def get_index_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return list(self.get_keyspace_meta(ksname).indexes)
-
-    def get_column_names(self, ksname, cfname):
-        if ksname is None:
-            ksname = self.current_keyspace
-        layout = self.get_table_meta(ksname, cfname)
-        return list(layout.columns)
-
-    def get_usertype_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return list(self.get_keyspace_meta(ksname).user_types)
-
-    def get_usertype_layout(self, ksname, typename):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        ks_meta = self.get_keyspace_meta(ksname)
-
-        try:
-            user_type = ks_meta.user_types[typename]
-        except KeyError:
-            raise UserTypeNotFound("User type {!r} not found".format(typename))
-
-        return list(zip(user_type.field_names, user_type.field_types))
-
-    def get_userfunction_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return [f.name for f in 
list(self.get_keyspace_meta(ksname).functions.values())]
-
-    def get_useraggregate_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return [f.name for f in 
list(self.get_keyspace_meta(ksname).aggregates.values())]
-
-    def get_cluster_name(self):
-        return self.conn.metadata.cluster_name
-
-    def get_partitioner(self):
-        return self.conn.metadata.partitioner
-
-    def get_keyspace_meta(self, ksname):
-        if ksname in self.conn.metadata.keyspaces:
-            return self.conn.metadata.keyspaces[ksname]
-
-        raise KeyspaceNotFound('Keyspace %r not found.' % ksname)
-
-    def get_keyspaces(self):
-        return list(self.conn.metadata.keyspaces.values())
-
-    def get_ring(self, ks):
-        self.conn.metadata.token_map.rebuild_keyspace(ks, build_if_absent=True)
-        return self.conn.metadata.token_map.tokens_to_hosts_by_ks[ks]
-
-    def get_table_meta(self, ksname, tablename):
-        if ksname is None:
-            ksname = self.current_keyspace
-        ksmeta = self.get_keyspace_meta(ksname)
-        if tablename not in ksmeta.tables:
-            if ksname == 'system_auth' and tablename in ['roles', 
'role_permissions']:
-                self.get_fake_auth_table_meta(ksname, tablename)
-            else:
-                raise ColumnFamilyNotFound("Column family {} not 
found".format(tablename))
-        else:
-            return ksmeta.tables[tablename]
-
-    def get_fake_auth_table_meta(self, ksname, tablename):
-        # may be using external auth implementation so internal tables
-        # aren't actually defined in schema. In this case, we'll fake
-        # them up
-        if tablename == 'roles':
-            ks_meta = KeyspaceMetadata(ksname, True, None, None)
-            table_meta = TableMetadata(ks_meta, 'roles')
-            table_meta.columns['role'] = ColumnMetadata(table_meta, 'role', 
cassandra.cqltypes.UTF8Type)
-            table_meta.columns['is_superuser'] = ColumnMetadata(table_meta, 
'is_superuser', cassandra.cqltypes.BooleanType)
-            table_meta.columns['can_login'] = ColumnMetadata(table_meta, 
'can_login', cassandra.cqltypes.BooleanType)
-        elif tablename == 'role_permissions':
-            ks_meta = KeyspaceMetadata(ksname, True, None, None)
-            table_meta = TableMetadata(ks_meta, 'role_permissions')
-            table_meta.columns['role'] = ColumnMetadata(table_meta, 'role', 
cassandra.cqltypes.UTF8Type)
-            table_meta.columns['resource'] = ColumnMetadata(table_meta, 
'resource', cassandra.cqltypes.UTF8Type)
-            table_meta.columns['permission'] = ColumnMetadata(table_meta, 
'permission', cassandra.cqltypes.UTF8Type)
-        else:
-            raise ColumnFamilyNotFound("Column family {} not 
found".format(tablename))
-
-    def get_index_meta(self, ksname, idxname):
-        if ksname is None:
-            ksname = self.current_keyspace
-        ksmeta = self.get_keyspace_meta(ksname)
-
-        if idxname not in ksmeta.indexes:
-            raise IndexNotFound("Index {} not found".format(idxname))
-
-        return ksmeta.indexes[idxname]
-
-    def get_view_meta(self, ksname, viewname):
-        if ksname is None:
-            ksname = self.current_keyspace
-        ksmeta = self.get_keyspace_meta(ksname)
-
-        if viewname not in ksmeta.views:
-            raise MaterializedViewNotFound("Materialized view '{}' not 
found".format(viewname))
-        return ksmeta.views[viewname]
-
-    def get_object_meta(self, ks, name):
-        if name is None:
-            if ks and ks in self.conn.metadata.keyspaces:
-                return self.conn.metadata.keyspaces[ks]
-            elif self.current_keyspace is None:
-                raise ObjectNotFound("'{}' not found in keyspaces".format(ks))
-            else:
-                name = ks
-                ks = self.current_keyspace
-
-        if ks is None:
-            ks = self.current_keyspace
-
-        ksmeta = self.get_keyspace_meta(ks)
-
-        if name in ksmeta.tables:
-            return ksmeta.tables[name]
-        elif name in ksmeta.indexes:
-            return ksmeta.indexes[name]
-        elif name in ksmeta.views:
-            return ksmeta.views[name]
-
-        raise ObjectNotFound("'{}' not found in keyspace '{}'".format(name, 
ks))
-
-    def get_trigger_names(self, ksname=None):
-        if ksname is None:
-            ksname = self.current_keyspace
-
-        return [trigger.name
-                for table in 
list(self.get_keyspace_meta(ksname).tables.values())
-                for trigger in list(table.triggers.values())]
-
-    def reset_statement(self):
-        self.reset_prompt()
-        self.statement.truncate(0)
-        self.statement.seek(0)
-        self.empty_lines = 0
-
-    def reset_prompt(self):
-        if self.current_keyspace is None:
-            self.set_prompt(self.default_prompt, True)
-        else:
-            
self.set_prompt(self.keyspace_prompt.format(self.current_keyspace), True)
-
-    def set_continue_prompt(self):
-        if self.empty_lines >= 3:
-            self.set_prompt("Statements are terminated with a ';'.  You can 
press CTRL-C to cancel an incomplete statement.")
-            self.empty_lines = 0
-            return
-        if self.current_keyspace is None:
-            self.set_prompt(self.continue_prompt)
-        else:
-            spaces = ' ' * len(str(self.current_keyspace))
-            self.set_prompt(self.keyspace_continue_prompt.format(spaces))
-        self.empty_lines = self.empty_lines + 1 if not self.lastcmd else 0
-
-    @contextmanager
-    def prepare_loop(self):
-        readline = None
-        if self.tty and self.completekey:
-            try:
-                import readline
-            except ImportError:
-                pass
-            else:
-                old_completer = readline.get_completer()
-                readline.set_completer(self.complete)
-                if readline.__doc__ is not None and 'libedit' in 
readline.__doc__:
-                    readline.parse_and_bind("bind -e")
-                    readline.parse_and_bind("bind '" + self.completekey + "' 
rl_complete")
-                    readline.parse_and_bind("bind ^R em-inc-search-prev")
-                else:
-                    readline.parse_and_bind(self.completekey + ": complete")
-        # start coverage collection if requested, unless in subshell
-        if self.coverage and not self.is_subshell:
-            # check for coveragerc file, write it if missing
-            if os.path.exists(CQL_DIR):
-                self.coveragerc_path = os.path.join(CQL_DIR, '.coveragerc')
-                covdata_path = os.path.join(CQL_DIR, '.coverage')
-                if not os.path.isfile(self.coveragerc_path):
-                    with open(self.coveragerc_path, 'w') as f:
-                        f.writelines(["[run]\n",
-                                      "concurrency = multiprocessing\n",
-                                      "data_file = {}\n".format(covdata_path),
-                                      "parallel = true\n"]
-                                     )
-                # start coverage
-                import coverage
-                self.cov = coverage.Coverage(config_file=self.coveragerc_path)
-                self.cov.start()
-        try:
-            yield
-        finally:
-            if readline is not None:
-                readline.set_completer(old_completer)
-            if self.coverage and not self.is_subshell:
-                self.stop_coverage()
-
-    def get_input_line(self, prompt=''):
-        if self.tty:
-            self.lastcmd = input(str(prompt))
-            line = self.lastcmd + '\n'
-        else:
-            self.lastcmd = self.stdin.readline()
-            line = self.lastcmd
-            if not len(line):
-                raise EOFError
-        self.lineno += 1
-        return line
-
-    def use_stdin_reader(self, until='', prompt=''):
-        until += '\n'
-        while True:
-            try:
-                newline = self.get_input_line(prompt=prompt)
-            except EOFError:
-                return
-            if newline == until:
-                return
-            yield newline
-
-    def cmdloop(self, intro=None):
-        """
-        Adapted from cmd.Cmd's version, because there is literally no way with
-        cmd.Cmd.cmdloop() to tell the difference between "EOF" showing up in
-        input and an actual EOF.
-        """
-        with self.prepare_loop():
-            while not self.stop:
-                try:
-                    if self.single_statement:
-                        line = self.single_statement
-                        self.stop = True
-                    else:
-                        line = self.get_input_line(self.prompt)
-                    self.statement.write(line)
-                    if self.onecmd(self.statement.getvalue()):
-                        self.reset_statement()
-                except EOFError:
-                    self.handle_eof()
-                except CQL_ERRORS as cqlerr:
-                    self.printerr(cqlerr.message)
-                except KeyboardInterrupt:
-                    self.reset_statement()
-                    print('')
-
-    def strip_comment_blocks(self, statementtext):
-        comment_block_in_literal_string = 
re.search('["].*[/][*].*[*][/].*["]', statementtext)
-        if not comment_block_in_literal_string:
-            result = re.sub('[/][*].*[*][/]', "", statementtext)
-            if '*/' in result and '/*' not in result and not self.in_comment:
-                raise SyntaxError("Encountered comment block terminator 
without being in comment block")
-            if '/*' in result:
-                result = re.sub('[/][*].*', "", result)
-                self.in_comment = True
-            if '*/' in result:
-                result = re.sub('.*[*][/]', "", result)
-                self.in_comment = False
-            if self.in_comment and not re.findall('[/][*]|[*][/]', 
statementtext):
-                result = ''
-            return result
-        return statementtext
-
-    def onecmd(self, statementtext):
-        """
-        Returns true if the statement is complete and was handled (meaning it
-        can be reset).
-        """
-        statementtext = self.strip_comment_blocks(statementtext)
-        try:
-            statements, endtoken_escaped = 
cqlruleset.cql_split_statements(statementtext)
-        except pylexotron.LexingError as e:
-            if self.show_line_nums:
-                self.printerr('Invalid syntax at line {0}, char {1}'
-                              .format(e.linenum, e.charnum))
-            else:
-                self.printerr('Invalid syntax at char {0}'.format(e.charnum))
-            statementline = statementtext.split('\n')[e.linenum - 1]
-            self.printerr('  {0}'.format(statementline))
-            self.printerr(' {0}^'.format(' ' * e.charnum))
-            return True
-
-        while statements and not statements[-1]:
-            statements = statements[:-1]
-        if not statements:
-            return True
-        if endtoken_escaped or statements[-1][-1][0] != 'endtoken':
-            self.set_continue_prompt()
-            return
-        for st in statements:
-            try:
-                self.handle_statement(st, statementtext)
-            except Exception as e:
-                if self.debug:
-                    traceback.print_exc()
-                else:
-                    self.printerr(e)
-        return True
-
-    def handle_eof(self):
-        if self.tty:
-            print('')
-        statement = self.statement.getvalue()
-        if statement.strip():
-            if not self.onecmd(statement):
-                self.printerr('Incomplete statement at end of file')
-        self.do_exit()
-
-    def handle_statement(self, tokens, srcstr):
-        # Concat multi-line statements and insert into history
-        if readline is not None:
-            nl_count = srcstr.count("\n")
-
-            new_hist = srcstr.replace("\n", " ").rstrip()
-
-            if nl_count > 1 and self.last_hist != new_hist:
-                readline.add_history(new_hist)
-
-            self.last_hist = new_hist
-        cmdword = tokens[0][1]
-        if cmdword == '?':
-            cmdword = 'help'
-        custom_handler = getattr(self, 'do_' + cmdword.lower(), None)
-        if custom_handler:
-            parsed = cqlruleset.cql_whole_parse_tokens(tokens, srcstr=srcstr,
-                                                       
startsymbol='cqlshCommand')
-            if parsed and not parsed.remainder:
-                # successful complete parse
-                return custom_handler(parsed)
-            else:
-                return self.handle_parse_error(cmdword, tokens, parsed, srcstr)
-        return self.perform_statement(cqlruleset.cql_extract_orig(tokens, 
srcstr))
-
-    def handle_parse_error(self, cmdword, tokens, parsed, srcstr):
-        if cmdword.lower() in ('select', 'insert', 'update', 'delete', 
'truncate',
-                               'create', 'drop', 'alter', 'grant', 'revoke',
-                               'batch', 'list'):
-            # hey, maybe they know about some new syntax we don't. type
-            # assumptions won't work, but maybe the query will.
-            return self.perform_statement(cqlruleset.cql_extract_orig(tokens, 
srcstr))
-        if parsed:
-            self.printerr('Improper %s command (problem at %r).' % (cmdword, 
parsed.remainder[0]))
-        else:
-            self.printerr(f'Improper {cmdword} command.')
-
-    def do_use(self, parsed):
-        ksname = parsed.get_binding('ksname')
-        success, _ = 
self.perform_simple_statement(SimpleStatement(parsed.extract_orig()))
-        if success:
-            if ksname[0] == '"' and ksname[-1] == '"':
-                self.current_keyspace = self.cql_unprotect_name(ksname)
-            else:
-                self.current_keyspace = ksname.lower()
-
-    def do_select(self, parsed):
-        tracing_was_enabled = self.tracing_enabled
-        ksname = parsed.get_binding('ksname')
-        stop_tracing = ksname == 'system_traces' or (ksname is None and 
self.current_keyspace == 'system_traces')
-        self.tracing_enabled = self.tracing_enabled and not stop_tracing
-        statement = parsed.extract_orig()
-        self.perform_statement(statement)
-        self.tracing_enabled = tracing_was_enabled
-
-    def perform_statement(self, 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)
-
-        if future:
-            if future.warnings:
-                self.print_warnings(future.warnings)
-
-            if self.tracing_enabled:
-                try:
-                    for trace in 
future.get_all_query_traces(max_wait_per=self.max_trace_wait, 
query_cl=self.consistency_level):
-                        print_trace(self, trace)
-                except TraceUnavailable:
-                    msg = "Statement trace did not complete within %d seconds; 
trace data may be incomplete." % (self.session.max_trace_wait,)
-                    self.writeresult(msg, color=RED)
-                    for trace_id in future.get_query_trace_ids():
-                        self.show_session(trace_id, partial_session=True)
-                except Exception as err:
-                    self.printerr("Unable to fetch query trace: %s" % 
(str(err),))
-
-        return success
-
-    def parse_for_select_meta(self, query_string):
-        try:
-            parsed = cqlruleset.cql_parse(query_string)[1]
-        except IndexError:
-            return None
-        ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
-        name = self.cql_unprotect_name(parsed.get_binding('cfname', None))
-        try:
-            return self.get_table_meta(ks, name)
-        except ColumnFamilyNotFound:
-            try:
-                return self.get_view_meta(ks, name)
-            except MaterializedViewNotFound:
-                raise ObjectNotFound("'{}' not found in keyspace 
'{}'".format(name, ks))
-
-    def parse_for_update_meta(self, query_string):
-        try:
-            parsed = cqlruleset.cql_parse(query_string)[1]
-        except IndexError:
-            return None
-        ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
-        cf = self.cql_unprotect_name(parsed.get_binding('cfname'))
-        return self.get_table_meta(ks, cf)
-
-    def perform_simple_statement(self, statement):
-        if not statement:
-            return False, None
-
-        future = self.session.execute_async(statement, 
trace=self.tracing_enabled)
-        result = None
-        try:
-            result = future.result()
-        except CQL_ERRORS as err:
-            err_msg = err.message if hasattr(err, 'message') else str(err)
-            self.printerr(str(err.__class__.__name__) + ": " + err_msg)
-        except Exception:
-            import traceback
-            self.printerr(traceback.format_exc())
-
-        # Even if statement failed we try to refresh schema if not agreed (see 
CASSANDRA-9689)
-        if not future.is_schema_agreed:
-            try:
-                self.conn.refresh_schema_metadata(5)  # will throw exception 
if there is a schema mismatch
-            except Exception:
-                self.printerr("Warning: schema version mismatch detected; 
check the schema versions of your "
-                              "nodes in system.local and system.peers.")
-                self.conn.refresh_schema_metadata(-1)
-
-        if result is None:
-            return False, None
-
-        if statement.query_string[:6].lower() == 'select':
-            self.print_result(result, 
self.parse_for_select_meta(statement.query_string))
-        elif statement.query_string.lower().startswith("list users") or 
statement.query_string.lower().startswith("list roles"):
-            self.print_result(result, self.get_table_meta('system_auth', 
'roles'))
-        elif statement.query_string.lower().startswith("list"):
-            self.print_result(result, self.get_table_meta('system_auth', 
'role_permissions'))
-        elif result:
-            # CAS INSERT/UPDATE
-            self.writeresult("")
-            self.print_static_result(result, 
self.parse_for_update_meta(statement.query_string), with_header=True, 
tty=self.tty)
-        self.flush_output()
-        return True, future
-
-    def print_result(self, result, table_meta):
-        self.decoding_errors = []
-
-        self.writeresult("")
-
-        def print_all(result, table_meta, tty):
-            # Return the number of rows in total
-            num_rows = 0
-            is_first = True
-            while True:
-                # Always print for the first page even it is empty
-                if result.current_rows or is_first:
-                    with_header = is_first or tty
-                    self.print_static_result(result, table_meta, with_header, 
tty, num_rows)
-                    num_rows += len(result.current_rows)
-                if result.has_more_pages:
-                    if self.shunted_query_out is None and tty:
-                        # Only pause when not capturing.
-                        input("---MORE---")
-                    result.fetch_next_page()
-                else:
-                    if not tty:
-                        self.writeresult("")
-                    break
-                is_first = False
-            return num_rows
-
-        num_rows = print_all(result, table_meta, self.tty)
-        self.writeresult("(%d rows)" % num_rows)
-
-        if self.decoding_errors:
-            for err in self.decoding_errors[:2]:
-                self.writeresult(err.message(), color=RED)
-            if len(self.decoding_errors) > 2:
-                self.writeresult('%d more decoding errors suppressed.'
-                                 % (len(self.decoding_errors) - 2), color=RED)
-
-    def print_static_result(self, result, table_meta, with_header, tty, 
row_count_offset=0):
-        if not result.column_names and not table_meta:
-            return
-
-        column_names = result.column_names or list(table_meta.columns.keys())
-        formatted_names = [self.myformat_colname(name, table_meta) for name in 
column_names]
-        if not result.current_rows:
-            # print header only
-            self.print_formatted_result(formatted_names, None, 
with_header=True, tty=tty)
-            return
-
-        cql_types = []
-        if result.column_types:
-            ks_name = table_meta.keyspace_name if table_meta else 
self.current_keyspace
-            ks_meta = self.conn.metadata.keyspaces.get(ks_name, None)
-            cql_types = [CqlType(cql_typename(t), ks_meta) for t in 
result.column_types]
-
-        formatted_values = [list(map(self.myformat_value, [row[c] for c in 
column_names], cql_types)) for row in result.current_rows]
-
-        if self.expand_enabled:
-            self.print_formatted_result_vertically(formatted_names, 
formatted_values, row_count_offset)
-        else:
-            self.print_formatted_result(formatted_names, formatted_values, 
with_header, tty)
-
-    def print_formatted_result(self, formatted_names, formatted_values, 
with_header, tty):
-        # determine column widths
-        widths = [n.displaywidth for n in formatted_names]
-        if formatted_values is not None:
-            for fmtrow in formatted_values:
-                for num, col in enumerate(fmtrow):
-                    widths[num] = max(widths[num], col.displaywidth)
-
-        # print header
-        if with_header:
-            header = ' | '.join(hdr.ljust(w, color=self.color) for (hdr, w) in 
zip(formatted_names, widths))
-            self.writeresult(' ' + header.rstrip())
-            self.writeresult('-%s-' % '-+-'.join('-' * w for w in widths))
-
-        # stop if there are no rows
-        if formatted_values is None:
-            self.writeresult("")
-            return
-
-        # print row data
-        for row in formatted_values:
-            line = ' | '.join(col.rjust(w, color=self.color) for (col, w) in 
zip(row, widths))
-            self.writeresult(' ' + line)
-
-        if tty:
-            self.writeresult("")
-
-    def print_formatted_result_vertically(self, formatted_names, 
formatted_values, row_count_offset):
-        max_col_width = max([n.displaywidth for n in formatted_names])
-        max_val_width = max([n.displaywidth for row in formatted_values for n 
in row])
-
-        # for each row returned, list all the column-value pairs
-        for i, row in enumerate(formatted_values):
-            self.writeresult("@ Row %d" % (row_count_offset + i + 1))
-            self.writeresult('-%s-' % '-+-'.join(['-' * max_col_width, '-' * 
max_val_width]))
-            for field_id, field in enumerate(row):
-                column = formatted_names[field_id].ljust(max_col_width, 
color=self.color)
-                value = field.ljust(field.displaywidth, color=self.color)
-                self.writeresult(' ' + " | ".join([column, value]))
-            self.writeresult('')
-
-    def print_warnings(self, warnings):
-        if warnings is None or len(warnings) == 0:
-            return
-
-        self.writeresult('')
-        self.writeresult('Warnings :')
-        for warning in warnings:
-            self.writeresult(warning)
-            self.writeresult('')
-
-    def emptyline(self):
-        pass
-
-    def parseline(self, line):
-        # this shouldn't be needed
-        raise NotImplementedError
-
-    def complete(self, text, state):
-        if readline is None:
-            return
-        if state == 0:
-            try:
-                self.completion_matches = self.find_completions(text)
-            except Exception:
-                if debug_completion:
-                    import traceback
-                    traceback.print_exc()
-                else:
-                    raise
-        try:
-            return self.completion_matches[state]
-        except IndexError:
-            return None
-
-    def find_completions(self, text):
-        curline = readline.get_line_buffer()
-        prevlines = self.statement.getvalue()
-        wholestmt = prevlines + curline
-        begidx = readline.get_begidx() + len(prevlines)
-        stuff_to_complete = wholestmt[:begidx]
-        return cqlruleset.cql_complete(stuff_to_complete, text, 
cassandra_conn=self,
-                                       debug=debug_completion, 
startsymbol='cqlshCommand')
-
-    def set_prompt(self, prompt, prepend_user=False):
-        if prepend_user and self.username:
-            self.prompt = "{0}@{1}".format(self.username, prompt)
-            return
-        self.prompt = prompt
-
-    def cql_unprotect_name(self, namestr):
-        if namestr is None:
-            return
-        return cqlruleset.dequote_name(namestr)
-
-    def cql_unprotect_value(self, valstr):
-        if valstr is not None:
-            return cqlruleset.dequote_value(valstr)
-
-    def _columnize_unicode(self, name_list):
-        """
-        Used when columnizing identifiers that may contain unicode
-        """
-        names = [n for n in name_list]
-        cmd.Cmd.columnize(self, names)
-        print('')
-
-    def do_describe(self, parsed):
-
-        """
-        DESCRIBE [cqlsh only]
-
-        (DESC may be used as a shorthand.)
-
-          Outputs information about the connected Cassandra cluster, or about
-          the data objects stored in the cluster. Use in one of the following 
ways:
-
-        DESCRIBE KEYSPACES
-
-          Output the names of all keyspaces.
-
-        DESCRIBE KEYSPACE [<keyspacename>]
-
-          Output CQL commands that could be used to recreate the given 
keyspace,
-          and the objects in it (such as tables, types, functions, etc.).
-          In some cases, as the CQL interface matures, there will be some 
metadata
-          about a keyspace that is not representable with CQL. That metadata 
will not be shown.
-          The '<keyspacename>' argument may be omitted, in which case the 
current
-          keyspace will be described.
-
-        DESCRIBE TABLES
-
-          Output the names of all tables in the current keyspace, or in all
-          keyspaces if there is no current keyspace.
-
-        DESCRIBE TABLE [<keyspace>.]<tablename>
-
-          Output CQL commands that could be used to recreate the given table.
-          In some cases, as above, there may be table metadata which is not
-          representable and which will not be shown.
-
-        DESCRIBE INDEX <indexname>
-
-          Output the CQL command that could be used to recreate the given 
index.
-          In some cases, there may be index metadata which is not representable
-          and which will not be shown.
-
-        DESCRIBE MATERIALIZED VIEW <viewname>
-
-          Output the CQL command that could be used to recreate the given 
materialized view.
-          In some cases, there may be materialized view metadata which is not 
representable
-          and which will not be shown.
-
-        DESCRIBE CLUSTER
-
-          Output information about the connected Cassandra cluster, such as the
-          cluster name, and the partitioner and snitch in use. When you are
-          connected to a non-system keyspace, also shows endpoint-range
-          ownership information for the Cassandra ring.
-
-        DESCRIBE [FULL] SCHEMA
-
-          Output CQL commands that could be used to recreate the entire 
(non-system) schema.
-          Works as though "DESCRIBE KEYSPACE k" was invoked for each 
non-system keyspace
-          k. Use DESCRIBE FULL SCHEMA to include the system keyspaces.
-
-        DESCRIBE TYPES
-
-          Output the names of all user-defined-types in the current keyspace, 
or in all
-          keyspaces if there is no current keyspace.
-
-        DESCRIBE TYPE [<keyspace>.]<type>
-
-          Output the CQL command that could be used to recreate the given 
user-defined-type.
-
-        DESCRIBE FUNCTIONS
-
-          Output the names of all user-defined-functions in the current 
keyspace, or in all
-          keyspaces if there is no current keyspace.
-
-        DESCRIBE FUNCTION [<keyspace>.]<function>
-
-          Output the CQL command that could be used to recreate the given 
user-defined-function.
-
-        DESCRIBE AGGREGATES
-
-          Output the names of all user-defined-aggregates in the current 
keyspace, or in all
-          keyspaces if there is no current keyspace.
-
-        DESCRIBE AGGREGATE [<keyspace>.]<aggregate>
-
-          Output the CQL command that could be used to recreate the given 
user-defined-aggregate.
-
-        DESCRIBE <objname>
-
-          Output CQL commands that could be used to recreate the entire object 
schema,
-          where object can be either a keyspace or a table or an index or a 
materialized
-          view (in this order).
-        """
-        stmt = SimpleStatement(parsed.extract_orig(), 
consistency_level=cassandra.ConsistencyLevel.LOCAL_ONE, 
fetch_size=self.page_size if self.use_paging else None)
-        future = self.session.execute_async(stmt)
-
-        if self.connection_versions['build'][0] < '4':
-            print('\nWARN: DESCRIBE|DESC was moved to server side in Cassandra 
4.0. As a consequence DESRIBE|DESC '
-                  'will not work in cqlsh %r connected to Cassandra %r, the 
version that you are connected to. '
-                  'DESCRIBE does not exist server side prior Cassandra 4.0.'
-                  % (version, self.connection_versions['build']))
-        else:
-            try:
-                result = future.result()
-
-                what = parsed.matched[1][1].lower()
-
-                if what in ('columnfamilies', 'tables', 'types', 'functions', 
'aggregates'):
-                    self.describe_list(result)
-                elif what == 'keyspaces':
-                    self.describe_keyspaces(result)
-                elif what == 'cluster':
-                    self.describe_cluster(result)
-                elif what:
-                    self.describe_element(result)
-
-            except CQL_ERRORS as err:
-                err_msg = err.message if hasattr(err, 'message') else str(err)
-                self.printerr(err_msg.partition("message=")[2].strip('"'))
-            except Exception:
-                import traceback
-                self.printerr(traceback.format_exc())
-
-            if future:
-                if future.warnings:
-                    self.print_warnings(future.warnings)
-
-    do_desc = do_describe
-
-    def describe_keyspaces(self, rows):
-        """
-        Print the output for a DESCRIBE KEYSPACES query
-        """
-        names = [r['name'] for r in rows]
-
-        print('')
-        cmd.Cmd.columnize(self, names)
-        print('')
-
-    def describe_list(self, rows):
-        """
-        Print the output for all the DESCRIBE queries for element names (e.g 
DESCRIBE TABLES, DESCRIBE FUNCTIONS ...)
-        """
-        keyspace = None
-        names = list()
-        for row in rows:
-            if row['keyspace_name'] != keyspace:
-                if keyspace is not None:
-                    self.print_keyspace_element_names(keyspace, names)
-
-                keyspace = row['keyspace_name']
-                names = list()
-
-            names.append(str(row['name']))
-
-        if keyspace is not None:
-            self.print_keyspace_element_names(keyspace, names)
-            print('')
-
-    def print_keyspace_element_names(self, keyspace, names):
-        print('')
-        if self.current_keyspace is None:
-            print('Keyspace %s' % (keyspace))
-            print('---------%s' % ('-' * len(keyspace)))
-        cmd.Cmd.columnize(self, names)
-
-    def describe_element(self, rows):
-        """
-        Print the output for all the DESCRIBE queries where an element name as 
been specified (e.g DESCRIBE TABLE, DESCRIBE INDEX ...)
-        """
-        for row in rows:
-            print('')
-            self.query_out.write(row['create_statement'])
-            print('')
-
-    def describe_cluster(self, rows):
-        """
-        Print the output for a DESCRIBE CLUSTER query.
-
-        If a specified keyspace was in use the returned ResultSet will 
contains a 'range_ownership' column,
-        otherwise not.
-        """
-        for row in rows:
-            print('\nCluster: %s' % row['cluster'])
-            print('Partitioner: %s' % row['partitioner'])
-            print('Snitch: %s\n' % row['snitch'])
-            if 'range_ownership' in row:
-                print("Range ownership:")
-                for entry in list(row['range_ownership'].items()):
-                    print(' %39s  [%s]' % (entry[0], ', '.join([host for host 
in entry[1]])))
-                print('')
-
-    def do_copy(self, parsed):
-        r"""
-        COPY [cqlsh only]
-
-          COPY x FROM: Imports CSV data into a Cassandra table
-          COPY x TO: Exports data from a Cassandra table in CSV format.
-
-        COPY <table_name> [ ( column [, ...] ) ]
-             FROM ( '<file_pattern_1, file_pattern_2, ... file_pattern_n>' | 
STDIN )
-             [ WITH <option>='value' [AND ...] ];
-
-        File patterns are either file names or valid python glob expressions, 
e.g. *.csv or folder/*.csv.
-
-        COPY <table_name> [ ( column [, ...] ) ]
-             TO ( '<filename>' | STDOUT )
-             [ WITH <option>='value' [AND ...] ];
-
-        Available common COPY options and defaults:
-
-          DELIMITER=','           - character that appears between records
-          QUOTE='"'               - quoting character to be used to quote 
fields
-          ESCAPE='\'              - character to appear before the QUOTE char 
when quoted
-          HEADER=false            - whether to ignore the first line
-          NULL=''                 - string that represents a null value
-          DATETIMEFORMAT=         - timestamp strftime format
-            '%Y-%m-%d %H:%M:%S%z'   defaults to time_format value in cqlshrc
-          MAXATTEMPTS=5           - the maximum number of attempts per batch 
or range
-          REPORTFREQUENCY=0.25    - the frequency with which we display status 
updates in seconds
-          DECIMALSEP='.'          - the separator for decimal values
-          THOUSANDSSEP=''         - the separator for thousands digit groups
-          BOOLSTYLE='True,False'  - the representation for booleans, case 
insensitive, specify true followed by false,
-                                    for example yes,no or 1,0
-          NUMPROCESSES=n          - the number of worker processes, by default 
the number of cores minus one
-                                    capped at 16
-          CONFIGFILE=''           - a configuration file with the same format 
as .cqlshrc (see the Python ConfigParser
-                                    documentation) where you can specify WITH 
options under the following optional
-                                    sections: [copy], [copy-to], [copy-from], 
[copy:ks.table], [copy-to:ks.table],
-                                    [copy-from:ks.table], where <ks> is your 
keyspace name and <table> is your table
-                                    name. Options are read from these 
sections, in the order specified
-                                    above, and command line options always 
override options in configuration files.
-                                    Depending on the COPY direction, only the 
relevant copy-from or copy-to sections
-                                    are used. If no configfile is specified 
then .cqlshrc is searched instead.
-          RATEFILE=''             - an optional file where to print the output 
statistics
-
-        Available COPY FROM options and defaults:
-
-          CHUNKSIZE=5000          - the size of chunks passed to worker 
processes
-          INGESTRATE=100000       - an approximate ingest rate in rows per 
second
-          MINBATCHSIZE=10         - the minimum size of an import batch
-          MAXBATCHSIZE=20         - the maximum size of an import batch
-          MAXROWS=-1              - the maximum number of rows, -1 means no 
maximum
-          SKIPROWS=0              - the number of rows to skip
-          SKIPCOLS=''             - a comma separated list of column names to 
skip
-          MAXPARSEERRORS=-1       - the maximum global number of parsing 
errors, -1 means no maximum
-          MAXINSERTERRORS=1000    - the maximum global number of insert 
errors, -1 means no maximum
-          ERRFILE=''              - a file where to store all rows that could 
not be imported, by default this is
-                                    import_ks_table.err where <ks> is your 
keyspace and <table> is your table name.
-          PREPAREDSTATEMENTS=True - whether to use prepared statements when 
importing, by default True. Set this to
-                                    False if you don't mind shifting data 
parsing to the cluster. The cluster will also
-                                    have to compile every batch statement. For 
large and oversized clusters
-                                    this will result in a faster import but 
for smaller clusters it may generate
-                                    timeouts.
-          TTL=3600                - the time to live in seconds, by default 
data will not expire
-
-        Available COPY TO options and defaults:
-
-          ENCODING='utf8'          - encoding for CSV output
-          PAGESIZE='1000'          - the page size for fetching results
-          PAGETIMEOUT=10           - the page timeout in seconds for fetching 
results
-          BEGINTOKEN=''            - the minimum token string to consider when 
exporting data
-          ENDTOKEN=''              - the maximum token string to consider when 
exporting data
-          MAXREQUESTS=6            - the maximum number of requests each 
worker process can work on in parallel
-          MAXOUTPUTSIZE='-1'       - the maximum size of the output file 
measured in number of lines,
-                                     beyond this maximum the output file will 
be split into segments,
-                                     -1 means unlimited.
-          FLOATPRECISION=5         - the number of digits displayed after the 
decimal point for cql float values
-          DOUBLEPRECISION=12       - the number of digits displayed after the 
decimal point for cql double values
-
-        When entering CSV data on STDIN, you can use the sequence "\."
-        on a line by itself to end the data input.
-        """
-
-        ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
-        if ks is None:
-            ks = self.current_keyspace
-            if ks is None:
-                raise NoKeyspaceError("Not in any keyspace.")
-        table = self.cql_unprotect_name(parsed.get_binding('cfname'))
-        columns = parsed.get_binding('colnames', None)
-        if columns is not None:
-            columns = list(map(self.cql_unprotect_name, columns))
-        else:
-            # default to all known columns
-            columns = self.get_column_names(ks, table)
-
-        fname = parsed.get_binding('fname', None)
-        if fname is not None:
-            fname = self.cql_unprotect_value(fname)
-
-        copyoptnames = list(map(str.lower, parsed.get_binding('optnames', ())))
-        copyoptvals = list(map(self.cql_unprotect_value, 
parsed.get_binding('optvals', ())))
-        opts = dict(list(zip(copyoptnames, copyoptvals)))
-
-        direction = parsed.get_binding('dir').upper()
-        if direction == 'FROM':
-            task = ImportTask(self, ks, table, columns, fname, opts, 
self.conn.protocol_version, CONFIG_FILE)
-        elif direction == 'TO':
-            task = ExportTask(self, ks, table, columns, fname, opts, 
self.conn.protocol_version, CONFIG_FILE)
-        else:
-            raise SyntaxError("Unknown direction %s" % direction)
-
-        task.run()
-
-    def do_show(self, parsed):
-        """
-        SHOW [cqlsh only]
-
-          Displays information about the current cqlsh session. Can be called 
in
-          the following ways:
-
-        SHOW VERSION
-
-          Shows the version and build of the connected Cassandra instance, as
-          well as the version of the CQL spec that the connected Cassandra
-          instance understands.
-
-        SHOW HOST
-
-          Shows where cqlsh is currently connected.
-
-        SHOW SESSION <sessionid>
-
-          Pretty-prints the requested tracing session.
-
-        SHOW REPLICAS <token> (<keyspace>)
-
-          Lists the replica nodes by IP address for the given token. The 
current
-          keyspace is used if one is not specified.
-        """
-        showwhat = parsed.get_binding('what').lower()
-        if showwhat == 'version':
-            self.get_connection_versions()
-            self.show_version()
-        elif showwhat == 'host':
-            self.show_host()
-        elif showwhat.startswith('session'):
-            session_id = parsed.get_binding('sessionid').lower()
-            self.show_session(UUID(session_id))
-        elif showwhat.startswith('replicas'):
-            token_id = parsed.get_binding('token')
-            keyspace = parsed.get_binding('keyspace')
-            self.show_replicas(token_id, keyspace)
-        else:
-            self.printerr('Wait, how do I show %r?' % (showwhat,))
-
-    def do_source(self, parsed):
-        """
-        SOURCE [cqlsh only]
-
-        Executes a file containing CQL statements. Gives the output for each
-        statement in turn, if any, or any errors that occur along the way.
-
-        Errors do NOT abort execution of the CQL source file.
-
-        Usage:
-
-          SOURCE '<file>';
-
-        That is, the path to the file to be executed must be given inside a
-        string literal. The path is interpreted relative to the current working
-        directory. The tilde shorthand notation ('~/mydir') is supported for
-        referring to $HOME.
-
-        See also the --file option to cqlsh.
-        """
-        fname = parsed.get_binding('fname')
-        fname = os.path.expanduser(self.cql_unprotect_value(fname))
-        try:
-            encoding, bom_size = get_file_encoding_bomsize(fname)
-            f = codecs.open(fname, 'r', encoding)
-            f.seek(bom_size)
-        except IOError as e:
-            self.printerr('Could not open %r: %s' % (fname, e))
-            return
-        subshell = Shell(self.hostname, self.port, color=self.color,
-                         username=self.username,
-                         encoding=self.encoding, stdin=f, tty=False, 
use_conn=self.conn,
-                         cqlver=self.cql_version, 
keyspace=self.current_keyspace,
-                         tracing_enabled=self.tracing_enabled,
-                         display_nanotime_format=self.display_nanotime_format,
-                         
display_timestamp_format=self.display_timestamp_format,
-                         display_date_format=self.display_date_format,
-                         display_float_precision=self.display_float_precision,
-                         
display_double_precision=self.display_double_precision,
-                         display_timezone=self.display_timezone,
-                         max_trace_wait=self.max_trace_wait, ssl=self.ssl,
-                         request_timeout=self.session.default_timeout,
-                         connect_timeout=self.conn.connect_timeout,
-                         is_subshell=True,
-                         auth_provider=self.auth_provider)
-        # duplicate coverage related settings in subshell
-        if self.coverage:
-            subshell.coverage = True
-            subshell.coveragerc_path = self.coveragerc_path
-        subshell.cmdloop()
-        f.close()
-
-    def do_capture(self, parsed):
-        """
-        CAPTURE [cqlsh only]
-
-        Begins capturing command output and appending it to a specified file.
-        Output will not be shown at the console while it is captured.
-
-        Usage:
-
-          CAPTURE '<file>';
-          CAPTURE OFF;
-          CAPTURE;
-
-        That is, the path to the file to be appended to must be given inside a
-        string literal. The path is interpreted relative to the current working
-        directory. The tilde shorthand notation ('~/mydir') is supported for
-        referring to $HOME.
-
-        Only query result output is captured. Errors and output from cqlsh-only
-        commands will still be shown in the cqlsh session.
-
-        To stop capturing output and show it in the cqlsh session again, use
-        CAPTURE OFF.
-
-        To inspect the current capture configuration, use CAPTURE with no
-        arguments.
-        """
-        fname = parsed.get_binding('fname')
-        if fname is None:
-            if self.shunted_query_out is not None:
-                print("Currently capturing query output to %r." % 
(self.query_out.name,))
-            else:
-                print("Currently not capturing query output.")
-            return
-
-        if fname.upper() == 'OFF':
-            if self.shunted_query_out is None:
-                self.printerr('Not currently capturing output.')
-                return
-            self.query_out.close()
-            self.query_out = self.shunted_query_out
-            self.color = self.shunted_color
-            self.shunted_query_out = None
-            del self.shunted_color
-            return
-
-        if self.shunted_query_out is not None:
-            self.printerr('Already capturing output to %s. Use CAPTURE OFF'
-                          ' to disable.' % (self.query_out.name,))
-            return
-
-        fname = os.path.expanduser(self.cql_unprotect_value(fname))
-        try:
-            f = open(fname, 'a')
-        except IOError as e:
-            self.printerr('Could not open %r for append: %s' % (fname, e))
-            return
-        self.shunted_query_out = self.query_out
-        self.shunted_color = self.color
-        self.query_out = f
-        self.color = False
-        print('Now capturing query output to %r.' % (fname,))
-
-    def do_tracing(self, parsed):
-        """
-        TRACING [cqlsh]
-
-          Enables or disables request tracing.
-
-        TRACING ON
-
-          Enables tracing for all further requests.
-
-        TRACING OFF
-
-          Disables tracing.
-
-        TRACING
-
-          TRACING with no arguments shows the current tracing status.
-        """
-        self.tracing_enabled = SwitchCommand("TRACING", 
"Tracing").execute(self.tracing_enabled, parsed, self.printerr)
-
-    def do_expand(self, parsed):
-        """
-        EXPAND [cqlsh]
-
-          Enables or disables expanded (vertical) output.
-
-        EXPAND ON
-
-          Enables expanded (vertical) output.
-
-        EXPAND OFF
-
-          Disables expanded (vertical) output.
-
-        EXPAND
-
-          EXPAND with no arguments shows the current value of expand setting.
-        """
-        self.expand_enabled = SwitchCommand("EXPAND", "Expanded 
output").execute(self.expand_enabled, parsed, self.printerr)
-
-    def do_consistency(self, parsed):
-        """
-        CONSISTENCY [cqlsh only]
-
-           Overrides default consistency level (default level is ONE).
-
-        CONSISTENCY <level>
-
-           Sets consistency level for future requests.
-
-           Valid consistency levels:
-
-           ANY, ONE, TWO, THREE, QUORUM, ALL, LOCAL_ONE, LOCAL_QUORUM, 
EACH_QUORUM, SERIAL and LOCAL_SERIAL.
-
-           SERIAL and LOCAL_SERIAL may be used only for SELECTs; will be 
rejected with updates.
-
-        CONSISTENCY
-
-           CONSISTENCY with no arguments shows the current consistency level.
-        """
-        level = parsed.get_binding('level')
-        if level is None:
-            print('Current consistency level is %s.' % 
(cassandra.ConsistencyLevel.value_to_name[self.consistency_level]))
-            return
-
-        self.consistency_level = 
cassandra.ConsistencyLevel.name_to_value[level.upper()]
-        print('Consistency level set to %s.' % (level.upper(),))
-
-    def do_serial(self, parsed):
-        """
-        SERIAL CONSISTENCY [cqlsh only]
-
-           Overrides serial consistency level (default level is SERIAL).
-
-        SERIAL CONSISTENCY <level>
-
-           Sets consistency level for future conditional updates.
-
-           Valid consistency levels:
-
-           SERIAL, LOCAL_SERIAL.
-
-        SERIAL CONSISTENCY
-
-           SERIAL CONSISTENCY with no arguments shows the current consistency 
level.
-        """
-        level = parsed.get_binding('level')
-        if level is None:
-            print('Current serial consistency level is %s.' % 
(cassandra.ConsistencyLevel.value_to_name[self.serial_consistency_level]))
-            return
-
-        self.serial_consistency_level = 
cassandra.ConsistencyLevel.name_to_value[level.upper()]
-        print('Serial consistency level set to %s.' % (level.upper(),))
-
-    def do_login(self, parsed):
-        """
-        LOGIN [cqlsh only]
-
-           Changes login information without requiring restart.
-
-        LOGIN <username> (<password>)
-
-           Login using the specified username. If password is specified, it 
will be used
-           otherwise, you will be prompted to enter.
-        """
-        username = parsed.get_binding('username')
-        password = parsed.get_binding('password')
-        if password is None:
-            password = getpass.getpass()
-        else:
-            password = password[1:-1]
-
-        auth_provider = PlainTextAuthProvider(username=username, 
password=password)
-
-        conn = Cluster(contact_points=(self.hostname,), port=self.port, 
cql_version=self.conn.cql_version,
-                       protocol_version=self.conn.protocol_version,
-                       auth_provider=auth_provider,
-                       ssl_options=self.conn.ssl_options,
-                       
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
-                       control_connection_timeout=self.conn.connect_timeout,
-                       connect_timeout=self.conn.connect_timeout)
-
-        if self.current_keyspace:
-            session = conn.connect(self.current_keyspace)
-        else:
-            session = conn.connect()
-
-        # Copy session properties
-        session.default_timeout = self.session.default_timeout
-        session.row_factory = self.session.row_factory
-        session.default_consistency_level = 
self.session.default_consistency_level
-        session.max_trace_wait = self.session.max_trace_wait
-
-        # Update after we've connected in case we fail to authenticate
-        self.conn = conn
-        self.auth_provider = auth_provider
-        self.username = username
-        self.session = session
-
-    def do_exit(self, parsed=None):
-        """
-        EXIT/QUIT [cqlsh only]
-
-        Exits cqlsh.
-        """
-        self.stop = True
-        if self.owns_connection:
-            self.conn.shutdown()
-    do_quit = do_exit
-
-    def do_clear(self, parsed):
-        """
-        CLEAR/CLS [cqlsh only]
-
-        Clears the console.
-        """
-        subprocess.call('clear', shell=True)
-    do_cls = do_clear
-
-    def do_debug(self, parsed):
-        import pdb
-        pdb.set_trace()
-
-    def get_help_topics(self):
-        topics = [t[3:] for t in dir(self) if t.startswith('do_') and 
getattr(self, t, None).__doc__]
-        for hide_from_help in ('quit',):
-            topics.remove(hide_from_help)
-        return topics
-
-    def columnize(self, slist, *a, **kw):
-        return cmd.Cmd.columnize(self, sorted([u.upper() for u in slist]), *a, 
**kw)
-
-    def do_help(self, parsed):
-        """
-        HELP [cqlsh only]
-
-        Gives information about cqlsh commands. To see available topics,
-        enter "HELP" without any arguments. To see help on a topic,
-        use "HELP <topic>".
-        """
-        topics = parsed.get_binding('topic', ())
-        if not topics:
-            shell_topics = [t.upper() for t in self.get_help_topics()]
-            self.print_topics("\nDocumented shell commands:", shell_topics, 
15, 80)
-            cql_topics = [t.upper() for t in cqldocs.get_help_topics()]
-            self.print_topics("CQL help topics:", cql_topics, 15, 80)
-            return
-        for t in topics:
-            if t.lower() in self.get_help_topics():
-                doc = getattr(self, 'do_' + t.lower()).__doc__
-                self.stdout.write(doc + "\n")
-            elif t.lower() in cqldocs.get_help_topics():
-                urlpart = cqldocs.get_help_topic(t)
-                if urlpart is not None:
-                    url = "%s#%s" % (CASSANDRA_CQL_HTML, urlpart)
-                    if self.browser is not None:
-                        opened = webbrowser.get(self.browser).open_new_tab(url)
-                    else:
-                        opened = webbrowser.open_new_tab(url)
-                    if not opened:
-                        self.printerr("*** No browser to display CQL help. URL 
for help topic %s : %s" % (t, url))
-            else:
-                self.printerr("*** No help on %s" % (t,))
-
-    def do_unicode(self, parsed):
-        """
-        Textual input/output
-
-        When control characters, or other characters which can't be encoded
-        in your current locale, are found in values of 'text' or 'ascii'
-        types, it will be shown as a backslash escape. If color is enabled,
-        any such backslash escapes will be shown in a different color from
-        the surrounding text.
-
-        Unicode code points in your data will be output intact, if the
-        encoding for your locale is capable of decoding them. If you prefer
-        that non-ascii characters be shown with Python-style "\\uABCD"
-        escape sequences, invoke cqlsh with an ASCII locale (for example,
-        by setting the $LANG environment variable to "C").
-        """
-
-    def do_paging(self, parsed):
-        """
-        PAGING [cqlsh]
-
-          Enables or disables query paging.
-
-        PAGING ON
-
-          Enables query paging for all further queries.
-
-        PAGING OFF
-
-          Disables paging.
-
-        PAGING
-
-          PAGING with no arguments shows the current query paging status.
-        """
-        (self.use_paging, requested_page_size) = SwitchCommandWithValue(
-            "PAGING", "Query paging", value_type=int).execute(self.use_paging, 
parsed, self.printerr)
-        if self.use_paging and requested_page_size is not None:
-            self.page_size = requested_page_size
-        if self.use_paging:
-            print(("Page size: {}".format(self.page_size)))
-        else:
-            self.page_size = self.default_page_size
-
-    def applycolor(self, text, color=None):
-        if not color or not self.color:
-            return text
-        return color + text + ANSI_RESET
-
-    def writeresult(self, text, color=None, newline=True, out=None):
-        if out is None:
-            out = self.query_out
-
-        # convert Exceptions, etc to text
-        if not isinstance(text, str):
-            text = str(text)
-
-        to_write = self.applycolor(text, color) + ('\n' if newline else '')
-        out.write(to_write)
-
-    def flush_output(self):
-        self.query_out.flush()
-
-    def printerr(self, text, color=RED, newline=True, shownum=None):
-        self.statement_error = True
-        if shownum is None:
-            shownum = self.show_line_nums
-        if shownum:
-            text = '%s:%d:%s' % (self.stdin.name, self.lineno, text)
-        self.writeresult(text, color, newline=newline, out=sys.stderr)
-
-    def stop_coverage(self):
-        if self.coverage and self.cov is not None:
-            self.cov.stop()
-            self.cov.save()
-            self.cov = None
-
-
-class SwitchCommand(object):
-    command = None
-    description = None
-
-    def __init__(self, command, desc):
-        self.command = command
-        self.description = desc
-
-    def execute(self, state, parsed, printerr):
-        switch = parsed.get_binding('switch')
-        if switch is None:
-            if state:
-                print("%s is currently enabled. Use %s OFF to disable"
-                      % (self.description, self.command))
-            else:
-                print("%s is currently disabled. Use %s ON to enable."
-                      % (self.description, self.command))
-            return state
-
-        if switch.upper() == 'ON':
-            if state:
-                printerr('%s is already enabled. Use %s OFF to disable.'
-                         % (self.description, self.command))
-                return state
-            print('Now %s is enabled' % (self.description,))
-            return True
-
-        if switch.upper() == 'OFF':
-            if not state:
-                printerr('%s is not enabled.' % (self.description,))
-                return state
-            print('Disabled %s.' % (self.description,))
-            return False
-
-
-class SwitchCommandWithValue(SwitchCommand):
-    """The same as SwitchCommand except it also accepts a value in place of ON.
-
-    This returns a tuple of the form: (SWITCH_VALUE, PASSED_VALUE)
-    eg: PAGING 50 returns (True, 50)
-        PAGING OFF returns (False, None)
-        PAGING ON returns (True, None)
-
-    The value_type must match for the PASSED_VALUE, otherwise it will return 
None.
-    """
-    def __init__(self, command, desc, value_type=int):
-        SwitchCommand.__init__(self, command, desc)
-        self.value_type = value_type
-
-    def execute(self, state, parsed, printerr):
-        binary_switch_value = SwitchCommand.execute(self, state, parsed, 
printerr)
-        switch = parsed.get_binding('switch')
-        try:
-            value = self.value_type(switch)
-            binary_switch_value = True
-        except (ValueError, TypeError):
-            value = None
-        return binary_switch_value, value
-
-
-def option_with_default(cparser_getter, section, option, default=None):
-    try:
-        return cparser_getter(section, option)
-    except configparser.Error:
-        return default
-
-
-def raw_option_with_default(configs, section, option, default=None):
-    """
-    Same (almost) as option_with_default() but won't do any string 
interpolation.
-    Useful for config values that include '%' symbol, e.g. time format string.
-    """
-    try:
-        return configs.get(section, option, raw=True)
-    except configparser.Error:
-        return default
-
-
-def should_use_color():
-    if not sys.stdout.isatty():
-        return False
-    if os.environ.get('TERM', '') in ('dumb', ''):
-        return False
-    try:
-        p = subprocess.Popen(['tput', 'colors'], stdout=subprocess.PIPE)
-        stdout, _ = p.communicate()
-        if int(stdout.strip()) < 8:
-            return False
-    except (OSError, ImportError, ValueError):
-        # oh well, we tried. at least we know there's a $TERM and it's
-        # not "dumb".
-        pass
-    return True
-
-
-def read_options(cmdlineargs, environment):
-    configs = configparser.ConfigParser()
-    configs.read(CONFIG_FILE)
-
-    rawconfigs = configparser.RawConfigParser()
-    rawconfigs.read(CONFIG_FILE)
-
-    username_from_cqlshrc = option_with_default(configs.get, 'authentication', 
'username')
-    password_from_cqlshrc = option_with_default(rawconfigs.get, 
'authentication', 'password')
-    if username_from_cqlshrc or password_from_cqlshrc:
-        if password_from_cqlshrc and not 
is_file_secure(os.path.expanduser(CONFIG_FILE)):
-            print("\nWarning: Password is found in an insecure cqlshrc file. 
The file is owned or readable by other users on the system.",
-                  end='', file=sys.stderr)
-        print("\nNotice: Credentials in the cqlshrc file is deprecated and 
will be ignored in the future."
-              "\nPlease use a credentials file to specify the username and 
password.\n", file=sys.stderr)
-
-    optvalues = optparse.Values()
-
-    optvalues.username = None
-    optvalues.password = None
-    optvalues.credentials = 
os.path.expanduser(option_with_default(configs.get, 'authentication', 
'credentials',
-                                                                   
os.path.join(CQL_DIR, 'credentials')))
-    optvalues.keyspace = option_with_default(configs.get, 'authentication', 
'keyspace')
-    optvalues.browser = option_with_default(configs.get, 'ui', 'browser', None)
-    optvalues.completekey = option_with_default(configs.get, 'ui', 
'completekey',
-                                                DEFAULT_COMPLETEKEY)
-    optvalues.color = option_with_default(configs.getboolean, 'ui', 'color')
-    optvalues.time_format = raw_option_with_default(configs, 'ui', 
'time_format',
-                                                    DEFAULT_TIMESTAMP_FORMAT)
-    optvalues.nanotime_format = raw_option_with_default(configs, 'ui', 
'nanotime_format',
-                                                        
DEFAULT_NANOTIME_FORMAT)
-    optvalues.date_format = raw_option_with_default(configs, 'ui', 
'date_format',
-                                                    DEFAULT_DATE_FORMAT)
-    optvalues.float_precision = option_with_default(configs.getint, 'ui', 
'float_precision',
-                                                    DEFAULT_FLOAT_PRECISION)
-    optvalues.double_precision = option_with_default(configs.getint, 'ui', 
'double_precision',
-                                                     DEFAULT_DOUBLE_PRECISION)
-    optvalues.field_size_limit = option_with_default(configs.getint, 'csv', 
'field_size_limit', csv.field_size_limit())
-    optvalues.max_trace_wait = option_with_default(configs.getfloat, 
'tracing', 'max_trace_wait',
-                                                   DEFAULT_MAX_TRACE_WAIT)
-    optvalues.timezone = option_with_default(configs.get, 'ui', 'timezone', 
None)
-
-    optvalues.debug = False
-
-    optvalues.coverage = False
-    if 'CQLSH_COVERAGE' in environment.keys():
-        optvalues.coverage = True
-
-    optvalues.file = None
-    optvalues.ssl = option_with_default(configs.getboolean, 'connection', 
'ssl', DEFAULT_SSL)
-    optvalues.encoding = option_with_default(configs.get, 'ui', 'encoding', 
UTF8)
-
-    optvalues.tty = option_with_default(configs.getboolean, 'ui', 'tty', 
sys.stdin.isatty())
-    optvalues.protocol_version = option_with_default(configs.getint, 
'protocol', 'version', None)
-    optvalues.cqlversion = option_with_default(configs.get, 'cql', 'version', 
None)
-    optvalues.connect_timeout = option_with_default(configs.getint, 
'connection', 'timeout', DEFAULT_CONNECT_TIMEOUT_SECONDS)
-    optvalues.request_timeout = option_with_default(configs.getint, 
'connection', 'request_timeout', DEFAULT_REQUEST_TIMEOUT_SECONDS)
-    optvalues.execute = None
-    optvalues.insecure_password_without_warning = False
-
-    (options, arguments) = parser.parse_args(cmdlineargs, values=optvalues)
-
-    # Credentials from cqlshrc will be expanded,
-    # credentials from the command line are also expanded if there is a 
space...
-    # we need the following so that these two scenarios will work
-    #   cqlsh --credentials=~/.cassandra/creds
-    #   cqlsh --credentials ~/.cassandra/creds
-    options.credentials = os.path.expanduser(options.credentials)
-
-    if not is_file_secure(options.credentials):
-        print("\nWarning: Credentials file '{0}' exists but is not used, 
because:"
-              "\n  a. the file owner is not the current user; or"
-              "\n  b. the file is readable by group or other."
-              "\nPlease ensure the file is owned by the current user and is 
not readable by group or other."
-              "\nOn a Linux or UNIX-like system, you often can do this by 
using the `chown` and `chmod` commands:"
-              "\n  chown YOUR_USERNAME credentials"
-              "\n  chmod 600 credentials\n".format(options.credentials),
-              file=sys.stderr)
-        options.credentials = ''  # ConfigParser.read() will ignore unreadable 
files
-
-    if not options.username:
-        credentials = 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
-        options.username = username_from_cqlshrc
-
-    if not options.password:
-        rawcredentials = configparser.RawConfigParser()
-        rawcredentials.read(options.credentials)
-
-        # handling password in the same way as username, priority cli > 
credentials > cqlshrc
-        options.password = option_with_default(rawcredentials.get, 
'plain_text_auth', 'password', password_from_cqlshrc)
-        options.password = password_from_cqlshrc
-    elif not options.insecure_password_without_warning:
-        print("\nWarning: Using a password on the command line interface can 
be insecure."
-              "\nRecommendation: use the credentials file to securely provide 
the password.\n", file=sys.stderr)
-
-    # Make sure some user values read from the command line are in unicode
-    options.execute = maybe_ensure_text(options.execute)
-    options.username = maybe_ensure_text(options.username)
-    options.password = maybe_ensure_text(options.password)
-    options.keyspace = maybe_ensure_text(options.keyspace)
-
-    hostname = option_with_default(configs.get, 'connection', 'hostname', 
DEFAULT_HOST)
-    port = option_with_default(configs.get, 'connection', 'port', DEFAULT_PORT)
-
-    try:
-        options.connect_timeout = int(options.connect_timeout)
-    except ValueError:
-        parser.error('"%s" is not a valid connect timeout.' % 
(options.connect_timeout,))
-        options.connect_timeout = DEFAULT_CONNECT_TIMEOUT_SECONDS
-
-    try:
-        options.request_timeout = int(options.request_timeout)
-    except ValueError:
-        parser.error('"%s" is not a valid request timeout.' % 
(options.request_timeout,))
-        options.request_timeout = DEFAULT_REQUEST_TIMEOUT_SECONDS
-
-    hostname = environment.get('CQLSH_HOST', hostname)
-    port = environment.get('CQLSH_PORT', port)
-
-    if len(arguments) > 0:
-        hostname = arguments[0]
-    if len(arguments) > 1:
-        port = arguments[1]
-
-    if options.file or options.execute:
-        options.tty = False
-
-    if options.execute and not options.execute.endswith(';'):
-        options.execute += ';'
-
-    if optvalues.color in (True, False):
-        options.color = optvalues.color
-    else:
-        if options.file is not None:
-            options.color = False
-        else:
-            options.color = should_use_color()
-
-    if options.cqlversion is not None:
-        options.cqlversion, cqlvertup = full_cql_version(options.cqlversion)
-        if cqlvertup[0] < 3:
-            parser.error('%r is not a supported CQL version.' % 
options.cqlversion)
-    options.cqlmodule = cql3handling
-
-    try:
-        port = int(port)
-    except ValueError:
-        parser.error('%r is not a valid port number.' % port)
-    return options, hostname, port
-
-
-def setup_cqlruleset(cqlmodule):
-    global cqlruleset
-    cqlruleset = cqlmodule.CqlRuleSet
-    cqlruleset.append_rules(cqlshhandling.cqlsh_extra_syntax_rules)
-    for rulename, termname, func in cqlshhandling.cqlsh_syntax_completers:
-        cqlruleset.completer_for(rulename, termname)(func)
-    
cqlruleset.commands_end_with_newline.update(cqlshhandling.my_commands_ending_with_newline)
-
-
-def setup_cqldocs(cqlmodule):
-    global cqldocs
-    cqldocs = cqlmodule.cqldocs
-
-
-def init_history():
-    if readline is not None:
-        try:
-            readline.read_history_file(HISTORY)
-        except IOError:
-            pass
-        delims = readline.get_completer_delims()
-        delims.replace("'", "")
-        delims += '.'
-        readline.set_completer_delims(delims)
-
-
-def save_history():
-    if readline is not None:
-        try:
-            readline.write_history_file(HISTORY)
-        except IOError:
-            pass
-
-
-def main(options, hostname, port):
-    setup_cqlruleset(options.cqlmodule)
-    setup_cqldocs(options.cqlmodule)
-    init_history()
-    csv.field_size_limit(options.field_size_limit)
-
-    if options.file is None:
-        stdin = None
-    else:
-        try:
-            encoding, bom_size = get_file_encoding_bomsize(options.file)
-            stdin = codecs.open(options.file, 'r', encoding)
-            stdin.seek(bom_size)
-        except IOError as e:
-            sys.exit("Can't open %r: %s" % (options.file, e))
-
-    if options.debug:
-        sys.stderr.write("Using CQL driver: %s\n" % (cassandra,))
-        sys.stderr.write("Using connect timeout: %s seconds\n" % 
(options.connect_timeout,))
-        sys.stderr.write("Using '%s' encoding\n" % (options.encoding,))
-        sys.stderr.write("Using ssl: %s\n" % (options.ssl,))
-
-    # create timezone based on settings, environment or auto-detection
-    timezone = None
-    if options.timezone or 'TZ' in os.environ:
-        try:
-            import pytz
-            if options.timezone:
-                try:
-                    timezone = pytz.timezone(options.timezone)
-                except Exception:
-                    sys.stderr.write("Warning: could not recognize timezone 
'%s' specified in cqlshrc\n\n" % (options.timezone))
-            if 'TZ' in os.environ:
-                try:
-                    timezone = pytz.timezone(os.environ['TZ'])
-                except Exception:
-                    sys.stderr.write("Warning: could not recognize timezone 
'%s' from environment value TZ\n\n" % (os.environ['TZ']))
-        except ImportError:
-            sys.stderr.write("Warning: Timezone defined and 'pytz' module for 
timezone conversion not installed. Timestamps will be displayed in UTC 
timezone.\n\n")
-
-    # try auto-detect timezone if tzlocal is installed
-    if not timezone:
-        try:
-            from tzlocal import get_localzone
-            timezone = get_localzone()
-        except ImportError:
-            # we silently ignore and fallback to UTC unless a custom timestamp 
format (which likely
-            # does contain a TZ part) was specified
-            if options.time_format != DEFAULT_TIMESTAMP_FORMAT:
-                sys.stderr.write("Warning: custom timestamp format specified 
in cqlshrc, "
-                                 + "but local timezone could not be 
detected.\n"
-                                 + "Either install Python 'tzlocal' module for 
auto-detection "
-                                 + "or specify client timezone in your 
cqlshrc.\n\n")
-
-    try:
-        shell = Shell(hostname,
-                      port,
-                      color=options.color,
-                      username=options.username,
-                      stdin=stdin,
-                      tty=options.tty,
-                      completekey=options.completekey,
-                      browser=options.browser,
-                      protocol_version=options.protocol_version,
-                      cqlver=options.cqlversion,
-                      keyspace=options.keyspace,
-                      display_timestamp_format=options.time_format,
-                      display_nanotime_format=options.nanotime_format,
-                      display_date_format=options.date_format,
-                      display_float_precision=options.float_precision,
-                      display_double_precision=options.double_precision,
-                      display_timezone=timezone,
-                      max_trace_wait=options.max_trace_wait,
-                      ssl=options.ssl,
-                      single_statement=options.execute,
-                      request_timeout=options.request_timeout,
-                      connect_timeout=options.connect_timeout,
-                      encoding=options.encoding,
-                      auth_provider=authproviderhandling.load_auth_provider(
-                          config_file=CONFIG_FILE,
-                          cred_file=options.credentials,
-                          username=options.username,
-                          password=options.password))
-    except KeyboardInterrupt:
-        sys.exit('Connection aborted.')
-    except CQL_ERRORS as e:
-        sys.exit('Connection error: %s' % (e,))
-    except VersionNotSupported as e:
-        sys.exit('Unsupported CQL version: %s' % (e,))
-    if options.debug:
-        shell.debug = True
-    if options.coverage:
-        shell.coverage = True
-        import signal
-
-        def handle_sighup():
-            shell.stop_coverage()
-            shell.do_exit()
-
-        signal.signal(signal.SIGHUP, handle_sighup)
-
-    shell.cmdloop()
-    save_history()
-
-    if shell.batch_mode and shell.statement_error:
-        sys.exit(2)
-
+from cqlshlib.cqlshmain import main
 
 # always call this regardless of module name: when a sub-process is spawned
 # on Windows then the module name is not __main__, see CASSANDRA-9304 (Windows 
support was dropped in CASSANDRA-16956)
-insert_driver_hooks()
 
 if __name__ == '__main__':
-    main(*read_options(sys.argv[1:], os.environ))
-
-# vim: set ft=python et ts=4 sw=4 :
+    main(sys.argv[1:], CASSANDRA_PATH)
diff --git a/bin/cqlsh.py b/pylib/cqlshlib/cqlshmain.py
similarity index 96%
copy from bin/cqlsh.py
copy to pylib/cqlshlib/cqlshmain.py
index 9a561eb200..c550ed84c6 100755
--- a/bin/cqlsh.py
+++ b/pylib/cqlshlib/cqlshmain.py
@@ -1,5 +1,3 @@
-#!/usr/bin/python3
-
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -20,30 +18,19 @@ import cmd
 import codecs
 import configparser
 import csv
-import errno
 import getpass
 import optparse
 import os
-import platform
 import re
-import stat
 import subprocess
 import sys
 import traceback
 import warnings
 import webbrowser
 from contextlib import contextmanager
-from glob import glob
 from io import StringIO
 from uuid import UUID
 
-if sys.version_info < (3, 6):
-    sys.exit("\ncqlsh requires Python 3.6+\n")
-
-# see CASSANDRA-10428
-if platform.python_implementation().startswith('Jython'):
-    sys.exit("\nCQL Shell does not run on Jython\n")
-
 UTF8 = 'utf-8'
 
 description = "CQL Shell for Apache Cassandra"
@@ -59,22 +46,6 @@ try:
 except ImportError:
     pass
 
-CQL_LIB_PREFIX = 'cassandra-driver-internal-only-'
-
-CASSANDRA_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 
'..')
-CASSANDRA_CQL_HTML_FALLBACK = 
'https://cassandra.apache.org/doc/latest/cql/index.html'
-
-# default location of local CQL.html
-if os.path.exists(CASSANDRA_PATH + '/doc/cql3/CQL.html'):
-    # default location of local CQL.html
-    CASSANDRA_CQL_HTML = 'file://' + CASSANDRA_PATH + '/doc/cql3/CQL.html'
-elif os.path.exists('/usr/share/doc/cassandra/CQL.html'):
-    # fallback to package file
-    CASSANDRA_CQL_HTML = 'file:///usr/share/doc/cassandra/CQL.html'
-else:
-    # fallback to online version
-    CASSANDRA_CQL_HTML = CASSANDRA_CQL_HTML_FALLBACK
-
 # On Linux, the Python webbrowser module uses the 'xdg-open' executable
 # to open a file/URL. But that only works, if the current session has been
 # opened from _within_ a desktop environment. I.e. 'xdg-open' will fail,
@@ -89,47 +60,9 @@ if webbrowser._tryorder and webbrowser._tryorder[0] == 
'xdg-open' and os.environ
     webbrowser._tryorder.remove('xdg-open')
     webbrowser._tryorder.append('xdg-open')
 
-# use bundled lib for python-cql if available. if there
-# is a ../lib dir, use bundled libs there preferentially.
-ZIPLIB_DIRS = [os.path.join(CASSANDRA_PATH, 'lib')]
-
-if platform.system() == 'Linux':
-    ZIPLIB_DIRS.append('/usr/share/cassandra/lib')
-
-if os.environ.get('CQLSH_NO_BUNDLED', ''):
-    ZIPLIB_DIRS = ()
-
-
-def find_zip(libprefix):
-    for ziplibdir in ZIPLIB_DIRS:
-        zips = glob(os.path.join(ziplibdir, libprefix + '*.zip'))
-        if zips:
-            return max(zips)   # probably the highest version, if multiple
-
-
-cql_zip = find_zip(CQL_LIB_PREFIX)
-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))
-
-# the driver needs dependencies
-third_parties = ('six-', 'pure_sasl-')
-
-for lib in third_parties:
-    lib_zip = find_zip(lib)
-    if lib_zip:
-        sys.path.insert(0, lib_zip)
-
 warnings.filterwarnings("ignore", r".*blist.*")
-try:
-    import cassandra
-except ImportError as e:
-    sys.exit("\nPython Cassandra driver not installed, or not on PYTHONPATH.\n"
-             'You might try "pip install cassandra-driver".\n\n'
-             'Python: %s\n'
-             'Module load path: %r\n\n'
-             'Error: %s\n' % (sys.executable, sys.path, e))
 
+import cassandra
 from cassandra.auth import PlainTextAuthProvider
 from cassandra.cluster import Cluster
 from cassandra.cqltypes import cql_typename
@@ -139,12 +72,6 @@ from cassandra.policies import WhiteListRoundRobinPolicy
 from cassandra.query import SimpleStatement, ordered_dict_factory, 
TraceUnavailable
 from cassandra.util import datetime_from_timestamp
 
-# cqlsh should run correctly when run out of a Cassandra source tree,
-# out of an unpacked Cassandra tarball, and after a proper package install.
-cqlshlibdir = os.path.join(CASSANDRA_PATH, 'pylib')
-if os.path.isdir(cqlshlibdir):
-    sys.path.insert(0, cqlshlibdir)
-
 from cqlshlib import cql3handling, pylexotron, sslhandling, cqlshhandling, 
authproviderhandling
 from cqlshlib.copyutil import ExportTask, ImportTask
 from cqlshlib.displaying import (ANSI_RESET, BLUE, COLUMN_NAME_COLORS, CYAN,
@@ -174,6 +101,7 @@ else:
 
 cqldocs = None
 cqlruleset = None
+CASSANDRA_CQL_HTML = None
 
 epilog = """Connects to %(DEFAULT_HOST)s:%(DEFAULT_PORT)d by default. These
 defaults can be changed by setting $CQLSH_HOST and/or $CQLSH_PORT. When a
@@ -226,10 +154,12 @@ parser.add_option('-v', action="version", help='Print the 
current version of cql
 parser.add_option("--insecure-password-without-warning", action='store_true', 
dest='insecure_password_without_warning',
                   help=optparse.SUPPRESS_HELP)
 
+# use cfoptions for config file
+
 opt_values = optparse.Values()
-(options, arguments) = parser.parse_args(sys.argv[1:], values=opt_values)
+(cfoptions, arguments) = parser.parse_args(sys.argv[1:], values=opt_values)
 
-# BEGIN history/config definition
+# BEGIN history config
 
 
 def mkdirp(path):
@@ -258,10 +188,14 @@ try:
 except OSError:
     print('\nWarning: Cannot create directory at `%s`. Command history will 
not be saved. Please check what was the environment property CQL_HISTORY set 
to.\n' % HISTORY_DIR)
 
+
+# END history config
+
+
 DEFAULT_CQLSHRC = os.path.expanduser(os.path.join('~', '.cassandra', 
'cqlshrc'))
 
-if hasattr(options, 'cqlshrc'):
-    CONFIG_FILE = os.path.expanduser(options.cqlshrc)
+if hasattr(cfoptions, 'cqlshrc'):
+    CONFIG_FILE = os.path.expanduser(cfoptions.cqlshrc)
     if not os.path.exists(CONFIG_FILE):
         print('\nWarning: Specified cqlshrc location `%s` does not exist.  
Using `%s` instead.\n' % (CONFIG_FILE, DEFAULT_CQLSHRC))
         CONFIG_FILE = DEFAULT_CQLSHRC
@@ -384,30 +318,6 @@ warnings.showwarning = show_warning_without_quoting_line
 warnings.filterwarnings('always', 
category=cql3handling.UnexpectedTableStructure)
 
 
-def insert_driver_hooks():
-
-    class DateOverFlowWarning(RuntimeWarning):
-        pass
-
-    # Native datetime types blow up outside of datetime.[MIN|MAX]_YEAR. We 
will fall back to an int timestamp
-    def deserialize_date_fallback_int(byts, protocol_version):
-        timestamp_ms = int64_unpack(byts)
-        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."))
-            return timestamp_ms
-
-    cassandra.cqltypes.DateType.deserialize = 
staticmethod(deserialize_date_fallback_int)
-
-    if hasattr(cassandra, 'deserializers'):
-        del cassandra.deserializers.DesDateType
-
-    # Return cassandra.cqltypes.EMPTY instead of None for empty values
-    cassandra.cqltypes.CassandraType.support_empty_values = True
-
-
 class Shell(cmd.Cmd):
     custom_prompt = os.getenv('CQLSH_PROMPT', '')
     if custom_prompt != '':
@@ -2099,7 +2009,7 @@ def should_use_color():
     return True
 
 
-def read_options(cmdlineargs, environment):
+def read_options(cmdlineargs, environment=os.environ):
     configs = configparser.ConfigParser()
     configs.read(CONFIG_FILE)
 
@@ -2267,6 +2177,22 @@ def setup_cqldocs(cqlmodule):
     cqldocs = cqlmodule.cqldocs
 
 
+def setup_docspath(path):
+    global CASSANDRA_CQL_HTML
+    CASSANDRA_CQL_HTML_FALLBACK = 
'https://cassandra.apache.org/doc/latest/cql/index.html'
+    #
+    # default location of local CQL.html
+    if os.path.exists(path + '/doc/cql3/CQL.html'):
+        # default location of local CQL.html
+        CASSANDRA_CQL_HTML = 'file://' + path + '/doc/cql3/CQL.html'
+    elif os.path.exists('/usr/share/doc/cassandra/CQL.html'):
+        # fallback to package file
+        CASSANDRA_CQL_HTML = 'file:///usr/share/doc/cassandra/CQL.html'
+    else:
+        # fallback to online version
+        CASSANDRA_CQL_HTML = CASSANDRA_CQL_HTML_FALLBACK
+
+
 def init_history():
     if readline is not None:
         try:
@@ -2287,7 +2213,36 @@ def save_history():
             pass
 
 
-def main(options, hostname, port):
+def insert_driver_hooks():
+
+    class DateOverFlowWarning(RuntimeWarning):
+        pass
+
+    # Display milliseconds when datetime overflows (CASSANDRA-10625), E.g., 
the year 10000.
+    # Native datetime types blow up outside of datetime.[MIN|MAX]_YEAR. We 
will fall back to an int timestamp
+    def deserialize_date_fallback_int(byts, protocol_version):
+        timestamp_ms = int64_unpack(byts)
+        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."))
+            return timestamp_ms
+
+    cassandra.cqltypes.DateType.deserialize = 
staticmethod(deserialize_date_fallback_int)
+
+    if hasattr(cassandra, 'deserializers'):
+        del cassandra.deserializers.DesDateType
+
+    # Return cassandra.cqltypes.EMPTY instead of None for empty values
+    cassandra.cqltypes.CassandraType.support_empty_values = True
+
+
+def main(cmdline, pkgpath):
+    insert_driver_hooks()
+    (options, hostname, port) = read_options(cmdline)
+
+    setup_docspath(pkgpath)
     setup_cqlruleset(options.cqlmodule)
     setup_cqldocs(options.cqlmodule)
     init_history()
@@ -2395,11 +2350,4 @@ def main(options, hostname, port):
         sys.exit(2)
 
 
-# always call this regardless of module name: when a sub-process is spawned
-# on Windows then the module name is not __main__, see CASSANDRA-9304 (Windows 
support was dropped in CASSANDRA-16956)
-insert_driver_hooks()
-
-if __name__ == '__main__':
-    main(*read_options(sys.argv[1:], os.environ))
-
 # vim: set ft=python et ts=4 sw=4 :


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

Reply via email to