diff --git a/.build/build-resolver.xml b/.build/build-resolver.xml
index 9ddc06e440da..9bf4c8ff8aea 100644
--- a/.build/build-resolver.xml
+++ b/.build/build-resolver.xml
@@ -205,7 +205,6 @@
-
@@ -239,7 +238,6 @@
-
diff --git a/README.asc b/README.asc
index 913c6ce55fef..e1e04a79b18a 100644
--- a/README.asc
+++ b/README.asc
@@ -12,7 +12,7 @@ For more information, see http://cassandra.apache.org/[the Apache Cassandra web
Requirements
------------
. Java >= 1.8 (OpenJDK and Oracle JVMS have been tested)
-. Python 3.6+ (for cqlsh; 2.7 works but is deprecated)
+. Python 3.8+ (for cqlsh)
Getting started
---------------
diff --git a/bin/cqlsh b/bin/cqlsh
index a962e1181de8..05d5a9bfa6f0 100755
--- a/bin/cqlsh
+++ b/bin/cqlsh
@@ -62,8 +62,8 @@ is_supported_version() {
version=$1
major_version="${version%.*}"
minor_version="${version#*.}"
- # python3.6+ is supported. python2.7 is deprecated but still compatible.
- if [ "$major_version" = 3 ] && [ "$minor_version" -ge 6 ] || [ "$version" = "2.7" ]; then
+ # python 3.8-3.12 is supported
+ if [ "$major_version" = 3 ] && [ "$minor_version" -ge 8 ] && [ "$minor_version" -le 12 ]; then
echo "supported"
else
echo "unsupported"
@@ -79,6 +79,8 @@ run_if_supported_version() {
if [ "$(is_supported_version "$version")" = "supported" ]; then
exec "$interpreter" "$($interpreter -c "import os; print(os.path.dirname(os.path.realpath('$0')))")/cqlsh.py" "$@"
exit
+ else
+ echo "Warning: unsupported version of Python, required 3.8-3.12 but found" $version >&2
fi
fi
}
diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index 8fd604d4dac1..5410a33b8d79 100755
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -34,8 +34,8 @@
from glob import glob
from uuid import UUID
-if sys.version_info < (3, 6) and sys.version_info[0:2] != (2, 7):
- sys.exit("\ncqlsh requires Python 3.6+ or Python 2.7 (deprecated)\n")
+if sys.version_info < (3, 8) or sys.version_info > (3, 12):
+ sys.exit("\ncqlsh requires Python 3.8-3.12\n")
# see CASSANDRA-10428
if platform.python_implementation().startswith('Jython'):
@@ -117,19 +117,15 @@ def find_zip(libprefix):
sys.path.insert(0, os.path.join(cql_zip, 'cassandra-driver-' + ver))
# the driver needs dependencies
-third_parties = ('futures-', 'six-', 'geomet-', 'pure_sasl-', 'datastax_db_*-')
+third_parties = ('geomet-', 'pure_sasl-', 'datastax_db_*-')
for lib in third_parties:
lib_zip = find_zip(lib)
if lib_zip:
sys.path.insert(0, lib_zip)
-# We cannot import six until we add its location to sys.path so the Python
-# interpreter can find it. Do not move this to the top.
-import six
-
-from six.moves import configparser, input
-from six import StringIO, ensure_text, ensure_str
+import configparser
+from io import StringIO
warnings.filterwarnings("ignore", r".*blist.*")
try:
@@ -369,7 +365,7 @@ def __repr__(self):
def maybe_ensure_text(val):
- return ensure_text(val) if val else val
+ return str(val) if val else val
class FormatError(DecodeError):
@@ -434,7 +430,7 @@ def deserialize_date_fallback_int(byts, protocol_version):
class Shell(cmd.Cmd):
- custom_prompt = ensure_text(os.getenv('CQLSH_PROMPT', ''))
+ custom_prompt = os.getenv('CQLSH_PROMPT', '')
if custom_prompt != '':
custom_prompt += "\n"
default_prompt = custom_prompt + "cqlsh> "
@@ -576,7 +572,6 @@ def __init__(self, hostname, port, color=False,
if tty:
self.reset_prompt()
- self.maybe_warn_py2()
self.report_connection()
print('Use HELP for help.')
else:
@@ -661,12 +656,6 @@ def show_version(self):
vers['cql'] = self.cql_version
print("[cqlsh %(shver)s | Cassandra %(build)s | CQL spec %(cql)s | Native protocol v%(protocol)s]" % vers)
- def maybe_warn_py2(self):
- py2_suppress_warn = 'CQLSH_NO_WARN_PY2'
- if sys.version_info[0:2] == (2, 7) and not os.environ.get(py2_suppress_warn):
- print("Python 2.7 support is deprecated. "
- "Install Python 3.6+ or set %s to suppress this message.\n" % (py2_suppress_warn,))
-
def show_session(self, sessionid, partial_session=False):
print_trace_session(self, self.tracing_style, self.session, sessionid, partial_session)
@@ -915,15 +904,14 @@ def prepare_loop(self):
def get_input_line(self, prompt=''):
if self.tty:
- self.lastcmd = input(ensure_str(prompt))
- line = ensure_text(self.lastcmd) + '\n'
+ self.lastcmd = input(str(prompt))
+ line = self.lastcmd + '\n'
else:
- self.lastcmd = ensure_text(self.stdin.readline())
+ self.lastcmd = self.stdin.readline()
line = self.lastcmd
if not len(line):
raise EOFError
self.lineno += 1
- line = ensure_text(line)
return line
def use_stdin_reader(self, until='', prompt=''):
@@ -984,7 +972,6 @@ def onecmd(self, statementtext):
Returns true if the statement is complete and was handled (meaning it
can be reset).
"""
- statementtext = ensure_text(statementtext)
statementtext = self.strip_comment_blocks(statementtext)
try:
statements, endtoken_escaped = cqlruleset.cql_split_statements(statementtext)
@@ -1030,7 +1017,7 @@ def handle_statement(self, tokens, srcstr):
if readline is not None:
nl_count = srcstr.count("\n")
- new_hist = ensure_str(srcstr.replace("\n", " ").rstrip())
+ new_hist = srcstr.replace("\n", " ").rstrip()
if nl_count > 1 and self.last_hist != new_hist:
readline.add_history(new_hist)
@@ -1081,7 +1068,6 @@ def do_select(self, parsed):
self.tracing_style = tracing_was_enabled
def perform_statement(self, statement):
- statement = ensure_text(statement)
stmt = SimpleStatement(statement, consistency_level=self.consistency_level, serial_consistency_level=self.serial_consistency_level, fetch_size=self.page_size if self.use_paging else None)
success, future = self.perform_simple_statement(stmt)
@@ -1133,7 +1119,7 @@ def perform_simple_statement(self, statement):
return False, None
def print_cql_error(err):
- err_msg = ensure_text(err.message if hasattr(err, 'message') else str(err))
+ err_msg = err.message if hasattr(err, 'message') else str(err)
self.printerr(str(err.__class__.__name__) + ": " + err_msg)
future = self.session.execute_async(statement, trace=self.tracing_style in ["full", "compact"])
@@ -1466,7 +1452,7 @@ def do_describe(self, parsed):
self.describe_element(result)
except CQL_ERRORS as err:
- err_msg = ensure_text(err.message if hasattr(err, 'message') else str(err))
+ err_msg = err.message if hasattr(err, 'message') else str(err)
self.printerr(err_msg.partition("message=")[2].strip('"'))
except Exception:
import traceback
@@ -1482,7 +1468,7 @@ def describe_keyspaces(self, rows):
"""
Print the output for a DESCRIBE KEYSPACES query
"""
- names = [ensure_str(r['name']) for r in rows]
+ names = [r['name'] for r in rows]
print('')
cmd.Cmd.columnize(self, names)
@@ -1502,7 +1488,7 @@ def describe_list(self, rows):
keyspace = row['keyspace_name']
names = list()
- names.append(ensure_str(row['name']))
+ names.append(str(row['name']))
if keyspace is not None:
self.print_keyspace_element_names(keyspace, names)
@@ -1644,7 +1630,7 @@ def do_copy(self, parsed):
if fname is not None:
fname = self.cql_unprotect_value(fname)
- copyoptnames = list(map(six.text_type.lower, parsed.get_binding('optnames', ())))
+ 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)))
@@ -2105,11 +2091,11 @@ def writeresult(self, text, color=None, newline=True, out=None):
out = self.query_out
# convert Exceptions, etc to text
- if not isinstance(text, six.text_type):
- text = "{}".format(text)
+ if not isinstance(text, str):
+ text = str(text)
to_write = self.applycolor(text, color) + ('\n' if newline else '')
- to_write = ensure_str(to_write)
+ to_write = str(to_write)
out.write(to_write)
def flush_output(self):
@@ -2227,7 +2213,7 @@ def should_use_color():
def read_options(cmdlineargs, environment):
- configs = configparser.SafeConfigParser() if sys.version_info < (3, 2) else configparser.ConfigParser()
+ configs = configparser.ConfigParser()
configs.read(CONFIG_FILE)
rawconfigs = configparser.RawConfigParser()
diff --git a/doc/modules/cassandra/pages/getting_started/installing.adoc b/doc/modules/cassandra/pages/getting_started/installing.adoc
index c8ddcd0095a9..f534834fdb15 100644
--- a/doc/modules/cassandra/pages/getting_started/installing.adoc
+++ b/doc/modules/cassandra/pages/getting_started/installing.adoc
@@ -32,8 +32,7 @@ verify that you have the correct version of java installed, type
Full support is effective Cassandra 4.0.2 version (https://issues.apache.org/jira/browse/CASSANDRA-16894[CASSANDRA-16894])
For more information, see
https://github.com/apache/cassandra/blob/trunk/NEWS.txt[NEWS.txt].
-* For using cqlsh, the latest version of
-Python 3.6+ or Python 2.7 (support deprecated). To verify
+* For using cqlsh, the latest version of Python 3.8+. To verify
that you have the correct version of Python installed, type
`python --version`.
diff --git a/lib/cassandra-driver-internal-only-3.26.0-df103d3a.zip b/lib/cassandra-driver-internal-only-3.26.0-df103d3a.zip
deleted file mode 100644
index ddb70e77f1c8..000000000000
Binary files a/lib/cassandra-driver-internal-only-3.26.0-df103d3a.zip and /dev/null differ
diff --git a/lib/cassandra-driver-internal-only-3.29.2.zip b/lib/cassandra-driver-internal-only-3.29.2.zip
new file mode 100644
index 000000000000..41f0848a2bd1
Binary files /dev/null and b/lib/cassandra-driver-internal-only-3.29.2.zip differ
diff --git a/pylib/Dockerfile.ubuntu.py2 b/pylib/Dockerfile.ubuntu.py2
deleted file mode 100644
index 93016f016a01..000000000000
--- a/pylib/Dockerfile.ubuntu.py2
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM ubuntu:bionic
-RUN apt-get update && apt-get install -y python-minimal
diff --git a/pylib/Dockerfile.ubuntu.py3 b/pylib/Dockerfile.ubuntu.py3
index 7bbb71521869..74f9e3c4b439 100644
--- a/pylib/Dockerfile.ubuntu.py3
+++ b/pylib/Dockerfile.ubuntu.py3
@@ -1,2 +1,2 @@
FROM ubuntu:bionic
-RUN apt-get update && apt-get install -y python3-minimal && update-alternatives --install /usr/bin/python python /usr/bin/python3.6 1
+RUN apt-get update && apt-get install -y python3.8-minimal && update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1
diff --git a/pylib/Dockerfile.ubuntu.py312 b/pylib/Dockerfile.ubuntu.py312
new file mode 100644
index 000000000000..09d2f41be5f7
--- /dev/null
+++ b/pylib/Dockerfile.ubuntu.py312
@@ -0,0 +1,2 @@
+FROM ubuntu:bionic
+RUN apt-get update && apt-get install -y python3.12-minimal && update-alternatives --install /usr/bin/python python /usr/bin/python3.12 1
diff --git a/pylib/Dockerfile.ubuntu.py37 b/pylib/Dockerfile.ubuntu.py37
deleted file mode 100644
index 7b0850826a1c..000000000000
--- a/pylib/Dockerfile.ubuntu.py37
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM ubuntu:bionic
-RUN apt-get update && apt-get install -y python3.7-minimal && update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1
diff --git a/pylib/Dockerfile.ubuntu.py38 b/pylib/Dockerfile.ubuntu.py38
deleted file mode 100644
index 74f9e3c4b439..000000000000
--- a/pylib/Dockerfile.ubuntu.py38
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM ubuntu:bionic
-RUN apt-get update && apt-get install -y python3.8-minimal && update-alternatives --install /usr/bin/python python /usr/bin/python3.8 1
diff --git a/pylib/README.asc b/pylib/README.asc
index b0b6c7dcf6bf..e99c430c9b6b 100644
--- a/pylib/README.asc
+++ b/pylib/README.asc
@@ -1,11 +1,10 @@
== Overview
This directory contains code primarily for cqlsh. cqlsh uses cqlshlib in this directory.
-Currently, cqlshlib supports Python 2 as well as Python 3. Support for Python 3 is relatively
-new.
+Currently, cqlshlib supports Python 3.8-3.12.
== Requirements
-. Python 3 and 2.7 (for cqlsh)
+. Python 3.8-3.12 (for cqlsh)
. virtualenv
. Docker (optional)
diff --git a/pylib/cqlshlib/copyutil.py b/pylib/cqlshlib/copyutil.py
index 557033615865..a332a5eaf194 100644
--- a/pylib/cqlshlib/copyutil.py
+++ b/pylib/cqlshlib/copyutil.py
@@ -27,7 +27,6 @@
import random
import re
import signal
-import six
import struct
import sys
import threading
@@ -40,14 +39,12 @@
from collections import defaultdict, namedtuple
from decimal import Decimal
from random import randint
-from io import BytesIO, StringIO
+from io import StringIO
from select import select
from uuid import UUID
-from six import ensure_str, ensure_text
-from six.moves import configparser
-from six.moves import range
-from six.moves.queue import Queue
+import configparser
+from queue import Queue
from cassandra import OperationTimedOut
from cassandra.cluster import DefaultConnection
@@ -337,9 +334,9 @@ def parse_options(self, opts, direction):
opts = self.clean_options(self.maybe_read_config_file(opts, direction))
dialect_options = dict()
- dialect_options['quotechar'] = ensure_str(opts.pop('quote', '"'))
- dialect_options['escapechar'] = ensure_str(opts.pop('escape', '\\'))
- dialect_options['delimiter'] = ensure_str(opts.pop('delimiter', ','))
+ dialect_options['quotechar'] = opts.pop('quote', '"')
+ dialect_options['escapechar'] = opts.pop('escape', '\\')
+ dialect_options['delimiter'] = opts.pop('delimiter', ',')
if dialect_options['quotechar'] == dialect_options['escapechar']:
dialect_options['doublequote'] = True
del dialect_options['escapechar']
@@ -347,7 +344,7 @@ def parse_options(self, opts, direction):
dialect_options['doublequote'] = False
copy_options = dict()
- copy_options['nullval'] = ensure_str(opts.pop('null', ''))
+ copy_options['nullval'] = opts.pop('null', '')
copy_options['header'] = bool(opts.pop('header', '').lower() == 'true')
copy_options['encoding'] = opts.pop('encoding', 'utf8')
copy_options['maxrequests'] = int(opts.pop('maxrequests', 6))
@@ -369,7 +366,7 @@ def parse_options(self, opts, direction):
copy_options['consistencylevel'] = shell.consistency_level
copy_options['decimalsep'] = opts.pop('decimalsep', '.')
copy_options['thousandssep'] = opts.pop('thousandssep', '')
- copy_options['boolstyle'] = [ensure_str(s.strip()) for s in opts.pop('boolstyle', 'True, False').split(',')]
+ copy_options['boolstyle'] = [s.strip() for s in opts.pop('boolstyle', 'True, False').split(',')]
copy_options['numprocesses'] = int(opts.pop('numprocesses', self.get_num_processes(16)))
copy_options['begintoken'] = opts.pop('begintoken', '')
copy_options['endtoken'] = opts.pop('endtoken', '')
@@ -573,7 +570,7 @@ def open(self):
if self.header:
writer = csv.writer(self.current_dest.output, **self.options.dialect)
- writer.writerow([ensure_str(c) for c in self.columns])
+ writer.writerow([str(c) for c in self.columns])
return True
@@ -1746,7 +1743,7 @@ def write_rows_to_csv(self, token_range, rows, cql_types):
return # no rows in this range
try:
- output = StringIO() if six.PY3 else BytesIO()
+ output = StringIO()
writer = csv.writer(output, **self.options.dialect)
for row in rows:
@@ -1776,7 +1773,7 @@ def format_value(self, val, cqltype):
float_precision=cqltype.precision, nullval=self.nullval, quote=False,
decimal_sep=self.decimal_sep, thousands_sep=self.thousands_sep,
boolean_styles=self.boolean_styles)
- return formatted if six.PY3 else formatted.encode('utf8')
+ return formatted
def close(self):
ChildProcess.close(self)
@@ -1916,7 +1913,7 @@ def _get_primary_key_statement(parent, table_meta):
select_query = 'SELECT * FROM %s.%s WHERE %s' % (protect_name(parent.ks),
protect_name(parent.table),
where_clause)
- return parent.session.prepare(ensure_str(select_query))
+ return parent.session.prepare(select_query)
@staticmethod
def unprotect(v):
@@ -1952,20 +1949,20 @@ def convert_blob(v, **_):
return BlobType(v[2:].decode("hex"))
def convert_text(v, **_):
- return ensure_str(v)
+ return str(v)
def convert_uuid(v, **_):
return UUID(v)
def convert_bool(v, **_):
- return True if v.lower() == ensure_str(self.boolean_styles[0]).lower() else False
+ return True if v.lower() == self.boolean_styles[0].lower() else False
def get_convert_integer_fcn(adapter=int):
"""
Return a slow and a fast integer conversion function depending on self.thousands_sep
"""
if self.thousands_sep:
- return lambda v, ct=cql_type: adapter(v.replace(self.thousands_sep, ensure_str('')))
+ return lambda v, ct=cql_type: adapter(v.replace(self.thousands_sep, ''))
else:
return lambda v, ct=cql_type: adapter(v)
@@ -1973,8 +1970,8 @@ def get_convert_decimal_fcn(adapter=float):
"""
Return a slow and a fast decimal conversion function depending on self.thousands_sep and self.decimal_sep
"""
- empty_str = ensure_str('')
- dot_str = ensure_str('.')
+ empty_str = ''
+ dot_str = '.'
if self.thousands_sep and self.decimal_sep:
return lambda v, ct=cql_type: adapter(v.replace(self.thousands_sep, empty_str).replace(self.decimal_sep, dot_str))
elif self.thousands_sep:
@@ -2038,14 +2035,8 @@ def paren_match(c1, c2):
def convert_datetime(val, **_):
try:
- if six.PY2:
- # Python 2 implementation
- tval = time.strptime(val, self.date_time_format)
- return timegm(tval) * 1e3 # scale seconds to millis for the raw value
- else:
- # Python 3 implementation
- dtval = datetime.datetime.strptime(val, self.date_time_format)
- return dtval.timestamp() * 1000
+ dtval = datetime.datetime.strptime(val, self.date_time_format)
+ return dtval.timestamp() * 1000
except ValueError:
pass # if it's not in the default format we try CQL formats
@@ -2096,8 +2087,8 @@ def convert_map(val, ct=cql_type):
"""
See ImmutableDict above for a discussion of why a special object is needed here.
"""
- split_format_str = ensure_str('{%s}')
- sep = ensure_str(':')
+ split_format_str = '{%s}'
+ sep = ':'
return ImmutableDict(frozenset((convert_mandatory(ct.subtypes[0], v[0]), convert(ct.subtypes[1], v[1]))
for v in [split(split_format_str % vv, sep=sep) for vv in split(val)]))
@@ -2116,8 +2107,8 @@ def convert_user_type(val, ct=cql_type):
Also note that it is possible that the subfield names in the csv are in the
wrong order, so we must sort them according to ct.fieldnames, see CASSANDRA-12959.
"""
- split_format_str = ensure_str('{%s}')
- sep = ensure_str(':')
+ split_format_str = '{%s}'
+ sep = ':'
vals = [v for v in [split(split_format_str % vv, sep=sep) for vv in split(val)]]
dict_vals = dict((unprotect(v[0]), v[1]) for v in vals)
sorted_converted_vals = [(n, convert(t, dict_vals[n]) if n in dict_vals else self.get_null_val())
@@ -2176,7 +2167,7 @@ def get_null_val(self):
or "NULL" otherwise. Note that for counters we never use prepared statements, so we
only check is_counter when use_prepared_statements is false.
"""
- return None if self.use_prepared_statements else (ensure_str("0") if self.is_counter else ensure_str("NULL"))
+ return None if self.use_prepared_statements else ("0" if self.is_counter else "NULL")
def convert_row(self, row):
"""
@@ -2463,7 +2454,6 @@ def make_params(self):
if self.ttl >= 0:
query += 'USING TTL %s' % (self.ttl,)
make_statement = self.wrap_make_statement(self.make_non_prepared_batch_statement)
- query = ensure_str(query)
conv = ImportConversion(self, table_meta, prepared_statement)
tm = TokenMap(self.ks, self.hostname, self.local_dc, self.session)
@@ -2531,12 +2521,12 @@ def make_counter_batch_statement(self, query, conv, batch, replicas):
set_clause = []
for i, value in enumerate(row):
if i in conv.primary_key_indexes:
- where_clause.append(ensure_text("{}={}").format(self.valid_columns[i], ensure_text(value)))
+ where_clause.append("{}={}".format(self.valid_columns[i], str(value)))
else:
- set_clause.append(ensure_text("{}={}+{}").format(self.valid_columns[i], self.valid_columns[i], ensure_text(value)))
+ set_clause.append("{}={}+{}".format(self.valid_columns[i], self.valid_columns[i], str(value)))
- full_query_text = query % (ensure_text(',').join(set_clause), ensure_text(' AND ').join(where_clause))
- statement.add(ensure_str(full_query_text))
+ full_query_text = query % (','.join(set_clause), ' AND '.join(where_clause))
+ statement.add(full_query_text)
return statement
def make_prepared_batch_statement(self, query, _, batch, replicas):
@@ -2560,7 +2550,7 @@ def make_non_prepared_batch_statement(self, query, _, batch, replicas):
statement = BatchStatement(batch_type=BatchType.UNLOGGED, consistency_level=self.consistency_level)
statement.replicas = replicas
statement.keyspace = self.ks
- field_sep = b',' if six.PY2 else ','
+ field_sep = ','
statement._statements_and_parameters = [(False, query % (field_sep.join(r),), ()) for r in batch['rows']]
return statement
diff --git a/pylib/cqlshlib/displaying.py b/pylib/cqlshlib/displaying.py
index ef076f76a9b3..9e57eeb9246c 100644
--- a/pylib/cqlshlib/displaying.py
+++ b/pylib/cqlshlib/displaying.py
@@ -43,7 +43,7 @@ def get_str(val):
return val
-class FormattedValue(object):
+class FormattedValue:
def __init__(self, strval, coloredval=None, displaywidth=None):
self.strval = strval
diff --git a/pylib/cqlshlib/driver.py b/pylib/cqlshlib/driver.py
index e0bc7cef951f..d6ca4a7183ee 100644
--- a/pylib/cqlshlib/driver.py
+++ b/pylib/cqlshlib/driver.py
@@ -14,7 +14,6 @@
import os
import random
-import six
import stat
from cassandra.cluster import Cluster
from cassandra.connection import UnixSocketEndPoint
@@ -114,7 +113,7 @@ def cluster_factory(host, whitelist_lbp=True, **kwargs):
def is_unix_socket(hostname):
- if isinstance(hostname, six.string_types) and os.path.exists(hostname):
+ if isinstance(hostname, str) and os.path.exists(hostname):
mode = os.stat(hostname).st_mode
return stat.S_ISSOCK(mode)
return False
diff --git a/pylib/cqlshlib/formatting.py b/pylib/cqlshlib/formatting.py
index 7dbd9869361a..f230bfedeadd 100644
--- a/pylib/cqlshlib/formatting.py
+++ b/pylib/cqlshlib/formatting.py
@@ -16,7 +16,6 @@
from __future__ import unicode_literals
-import binascii
import calendar
import datetime
import math
@@ -25,8 +24,6 @@
import sys
import platform
-from six import ensure_text
-
from collections import defaultdict
from cassandra.cqltypes import EMPTY
@@ -126,7 +123,7 @@ def __init__(self, timestamp_format=DEFAULT_TIMESTAMP_FORMAT, date_format=DEFAUL
self.milliseconds_only = milliseconds_only # the microseconds part, .NNNNNN, wil be rounded to .NNN
-class CqlType(object):
+class CqlType:
"""
A class for converting a string into a cql type name that can match a formatter
and a list of its sub-types, if any.
@@ -213,7 +210,7 @@ def parse_sub_types(val, ksmeta):
def format_value_default(val, colormap, **_):
- val = ensure_text(str(val))
+ val = str(val)
escapedval = val.replace('\\', '\\\\')
bval = controlchars_re.sub(_show_control_chars, escapedval)
return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap)
@@ -245,7 +242,7 @@ def registrator(f):
return registrator
-class BlobType(object):
+class BlobType:
def __init__(self, val):
self.val = val
@@ -255,7 +252,7 @@ def __str__(self):
@formatter_for('BlobType')
def format_value_blob(val, colormap, **_):
- bval = ensure_text('0x') + ensure_text(binascii.hexlify(val))
+ bval = '0x' + val.hex()
return colorme(bval, colormap, 'blob')
@@ -265,7 +262,7 @@ def format_value_blob(val, colormap, **_):
def format_python_formatted_type(val, colormap, color, quote=False):
- bval = ensure_text(str(val))
+ bval = str(val)
if quote:
bval = "'%s'" % bval
return colorme(bval, colormap, color)
@@ -335,7 +332,7 @@ def format_floating_point_type(val, colormap, float_precision, decimal_sep=None,
def format_integer_type(val, colormap, thousands_sep=None, **_):
# base-10 only for now; support others?
bval = format_integer_with_thousands_sep(val, thousands_sep) if thousands_sep else str(val)
- bval = ensure_text(bval)
+ bval = str(bval)
return colorme(bval, colormap, 'int')
@@ -370,7 +367,7 @@ def format_value_timestamp(val, colormap, date_time_format, quote=False, **_):
if date_time_format.milliseconds_only:
bval = round_microseconds(bval)
else:
- bval = ensure_text(str(val))
+ bval = str(val)
if quote:
bval = "'%s'" % bval
diff --git a/pylib/cqlshlib/helptopics.py b/pylib/cqlshlib/helptopics.py
index 46cd1561e639..9be56b9f2ec7 100644
--- a/pylib/cqlshlib/helptopics.py
+++ b/pylib/cqlshlib/helptopics.py
@@ -15,7 +15,7 @@
# limitations under the License.
-class CQL3HelpTopics(object):
+class CQL3HelpTopics:
def get_help_topics(self):
return [t[5:] for t in dir(self) if t.startswith('help_')]
diff --git a/pylib/cqlshlib/saferscanner.py b/pylib/cqlshlib/saferscanner.py
index 6d7ba571afde..2c2b6100d9fb 100644
--- a/pylib/cqlshlib/saferscanner.py
+++ b/pylib/cqlshlib/saferscanner.py
@@ -19,7 +19,6 @@
# regex in-pattern flags. Any of those can break correct operation of Scanner.
import re
-import six
try:
from sre_constants import BRANCH, SUBPATTERN, GROUPREF, GROUPREF_IGNORE, GROUPREF_EXISTS
except ImportError:
@@ -53,23 +52,6 @@ def scrub_sub(cls, sub, flags):
return re.sre_parse.SubPattern(sub.pattern, scrubbedsub)
-class Py2SaferScanner(SaferScannerBase):
-
- def __init__(self, lexicon, flags=0):
- self.lexicon = lexicon
- p = []
- s = re.sre_parse.Pattern()
- s.flags = flags
- for phrase, action in lexicon:
- p.append(re.sre_parse.SubPattern(s, [
- (SUBPATTERN, (len(p) + 1, self.subpat(phrase, flags))),
- ]))
- s.groups = len(p) + 1
- p = re.sre_parse.SubPattern(s, [(BRANCH, (None, p))])
- self.p = p
- self.scanner = re.sre_compile.compile(p)
-
-
class Py36SaferScanner(SaferScannerBase):
def __init__(self, lexicon, flags=0):
@@ -118,7 +100,7 @@ def __init__(self, lexicon, flags=0):
self.scanner = re._compiler.compile(p)
-SaferScanner = Py36SaferScanner if six.PY3 else Py2SaferScanner
+SaferScanner = Py36SaferScanner
if version_info >= (3, 11):
SaferScanner = Py311SaferScanner
elif version_info >= (3, 8):
diff --git a/pylib/cqlshlib/sslhandling.py b/pylib/cqlshlib/sslhandling.py
index 8a7592c117dc..e4e1c6ae843a 100644
--- a/pylib/cqlshlib/sslhandling.py
+++ b/pylib/cqlshlib/sslhandling.py
@@ -14,12 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import configparser
import os
import sys
import ssl
-from six.moves import configparser
-
def ssl_settings(host, config_file, env=os.environ):
"""
@@ -39,7 +38,7 @@ def ssl_settings(host, config_file, env=os.environ):
either in the config file or as an environment variable.
Environment variables override any options set in cqlsh config file.
"""
- configs = configparser.SafeConfigParser()
+ configs = configparser.ConfigParser()
configs.read(config_file)
def get_option(section, option):
diff --git a/pylib/cqlshlib/test/ansi_colors.py b/pylib/cqlshlib/test/ansi_colors.py
index 9fc341154c5f..e8f6f35bdcbe 100644
--- a/pylib/cqlshlib/test/ansi_colors.py
+++ b/pylib/cqlshlib/test/ansi_colors.py
@@ -17,7 +17,6 @@
from __future__ import unicode_literals
import re
-import six
LIGHT = 0o10
@@ -106,7 +105,7 @@ def colortag(self):
class ColoredText(object):
def __init__(self, source=''):
- if isinstance(source, six.text_type):
+ if isinstance(source, str):
plain, colors = self.parse_ansi_colors(source)
self.chars = list(map(ColoredChar, plain, colors))
else:
diff --git a/pylib/cqlshlib/test/run_cqlsh.py b/pylib/cqlshlib/test/run_cqlsh.py
index f592cc078f28..8f3d4c25fe0f 100644
--- a/pylib/cqlshlib/test/run_cqlsh.py
+++ b/pylib/cqlshlib/test/run_cqlsh.py
@@ -40,14 +40,10 @@ def is_win():
import pty
DEFAULT_PREFIX = os.linesep
-DEFAULT_CQLSH_PROMPT = DEFAULT_PREFIX + '(\S+@)?cqlsh(:\S+)?> '
+DEFAULT_CQLSH_PROMPT = DEFAULT_PREFIX + r'(\S+@)?cqlsh(:\S+)?> '
DEFAULT_CQLSH_TERM = 'xterm'
-try:
- Pattern = re._pattern_type
-except AttributeError:
- # Python 3.7+
- Pattern = re.Pattern
+Pattern = re.Pattern
def get_smm_sequence(term='xterm'):
"""
diff --git a/pylib/cqlshlib/test/test_cqlsh_output.py b/pylib/cqlshlib/test/test_cqlsh_output.py
index 5044be92ef28..83c07eaea3f9 100644
--- a/pylib/cqlshlib/test/test_cqlsh_output.py
+++ b/pylib/cqlshlib/test/test_cqlsh_output.py
@@ -24,7 +24,6 @@
import re
import subprocess
import sys
-import six
import unittest
from .basecase import (BaseTestCase, TEST_HOST, TEST_PORT,
@@ -805,23 +804,6 @@ def test_show_output(self):
self.assertRegex(output, '^Connected to .* at %s:%d$'
% (re.escape(TEST_HOST), TEST_PORT))
- @unittest.skipIf(six.PY3, 'Will not emit warning when running Python 3')
- def test_warn_py2(self):
- # has the warning
- with testrun_cqlsh(tty=True, env=self.default_env) as c:
- self.assertIn('Python 2.7 support is deprecated.', c.output_header, 'cqlsh did not output expected warning.')
-
- # can suppress
- env = self.default_env.copy()
- env['CQLSH_NO_WARN_PY2'] = '1'
- with testrun_cqlsh(tty=True, env=env) as c:
- self.assertNotIn('Python 2.7 support is deprecated.', c.output_header, 'cqlsh did not output expected warning.')
-
- @unittest.skipIf(six.PY2, 'Warning will be emitted when running Python 2.7')
- def test_no_warn_py3(self):
- with testrun_cqlsh(tty=True, env=self.default_env) as c:
- self.assertNotIn('Python 2.7 support is deprecated.', c.output_header, 'cqlsh did not output expected warning.')
-
@unittest.skipIf(sys.platform == "win32", 'EOF signaling not supported on Windows')
def test_eof_prints_newline(self):
with testrun_cqlsh(tty=True, env=self.default_env) as c:
diff --git a/pylib/cqlshlib/test/winpty.py b/pylib/cqlshlib/test/winpty.py
index f197aa5b4533..593b76d755e8 100644
--- a/pylib/cqlshlib/test/winpty.py
+++ b/pylib/cqlshlib/test/winpty.py
@@ -15,8 +15,8 @@
# limitations under the License.
from threading import Thread
-from six import StringIO
-from six.moves.queue import Queue, Empty
+from io import StringIO
+from queue import Queue, Empty
class WinPty(object):
diff --git a/pylib/cqlshlib/tracing.py b/pylib/cqlshlib/tracing.py
index 17ca964dd75a..223dac74a6f2 100644
--- a/pylib/cqlshlib/tracing.py
+++ b/pylib/cqlshlib/tracing.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from datetime import datetime, timedelta
+from datetime import datetime
import time
from cassandra.query import QueryTrace, TraceUnavailable
diff --git a/pylib/cqlshlib/util.py b/pylib/cqlshlib/util.py
index 5df7edef62af..144586aae051 100644
--- a/pylib/cqlshlib/util.py
+++ b/pylib/cqlshlib/util.py
@@ -23,7 +23,7 @@
import stat
from datetime import timedelta, tzinfo
-from six import StringIO
+from io import StringIO
try:
from line_profiler import LineProfiler
diff --git a/pylib/requirements.txt b/pylib/requirements.txt
index 3c2e41b85c03..8b0e444abe8f 100644
--- a/pylib/requirements.txt
+++ b/pylib/requirements.txt
@@ -1,8 +1,4 @@
-# See python driver docs: six have to be installed before
-# cythonizing the driver, perhaps only on old pips.
-# http://datastax.github.io/python-driver/installation.html#cython-based-extensions
-six>=1.12.0
--e git+https://github.com/datastax/python-driver.git@cassandra-test#egg=cassandra-driver
+-e git+https://github.com/datastax/python-driver.git@3.29.2#egg=cassandra-driver
# Used ccm version is tracked by cassandra-test branch in ccm repo. Please create a PR there for fixes or upgrades to new releases.
-e git+https://github.com/datastax/cassandra-ccm.git@converged-cassandra#egg=ccm
coverage