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