diff --git a/NEWS.txt b/NEWS.txt index 3fd26e49c8..d1ca9a5505 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -124,6 +124,7 @@ New features Upgrading --------- + - cqlsh shell startup script now prefers 'python3' before 'python' when identifying a runtime. - As part of the Internode Messaging improvement work in CASSANDRA-15066, matching response verbs for every request verb were introduced and verbs were renamed. DroppedMessageMetrics pre-4.0 are now available with _REQ suffix. As part of CASSANDRA-16083, we added DroppedMessageMetrics backward compatibility layer which exposes the metrics with @@ -290,6 +291,7 @@ Deprecation - The JMX MBean org.apache.cassandra.db:type=BlacklistedDirectories has been deprecated in favor of org.apache.cassandra.db:type=DisallowedDirectories and will be removed in a subsequent major version. + - cqlsh support of 2.7 is deprecated and will warn when running with Python 2.7. Materialized Views ------------------- diff --git a/README.asc b/README.asc index 194b1eeb2a..2bec44f285 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 2.7 (for cqlsh) +. Python 3.6+ (for cqlsh; 2.7 works but is deprecated) Getting started --------------- diff --git a/bin/cqlsh b/bin/cqlsh index 0774d5297c..d13acf785b 100755 --- a/bin/cqlsh +++ b/bin/cqlsh @@ -53,17 +53,16 @@ fi # get a version string for a Python interpreter get_python_version() { interpreter=$1 - version=$(command -v "$interpreter" > /dev/null 2>&1 && $interpreter -c "import os; print('{}.{}'.format(os.sys.version_info.major, os.sys.version_info.minor))") + version=$($interpreter -c "import os; print('{}.{}'.format(os.sys.version_info.major, os.sys.version_info.minor))" 2> /dev/null) echo "$version" } # test whether a version string matches one of the supported versions for cqlsh is_supported_version() { version=$1 - # shellcheck disable=SC2039 # shellcheck disable=SC2072 - # python2.7 or python3.6+ is supported - if [ "$version" = "3.6" ] || [ "$version" \> "3.6" ] || [ "$version" = "2.7" ]; then + # python3.6+ is supported. python2.7 is deprecated but still compatible. + if [ ! "$version" \< "3.6" ] || [ "$version" = "2.7" ]; then echo "supported" else echo "unsupported" @@ -88,8 +87,7 @@ if [ "$USER_SPECIFIED_PYTHON" != "" ]; then # run a user specified Python interpreter run_if_supported_version "$USER_SPECIFIED_PYTHON" "$@" else - # try unqualified python first, then python3, then python2.7 - for interpreter in python python3 python2.7; do + for interpreter in python3 python python2.7; do run_if_supported_version "$interpreter" "$@" done fi diff --git a/bin/cqlsh.py b/bin/cqlsh.py old mode 100644 new mode 100755 index 09183a782d..dd9d95fb53 --- a/bin/cqlsh.py +++ b/bin/cqlsh.py @@ -1,5 +1,4 @@ -#!/bin/sh -# -*- mode: Python -*- +#!/usr/bin/python3 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -17,20 +16,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -""":" -# bash code here; finds a suitable python interpreter and execs this file. -# this implementation of cqlsh is compatible with both Python 3 and Python 2.7. -# prefer unqualified "python" if suitable: -python -c 'import sys; sys.exit(not (0x020700b0 < sys.hexversion))' 2>/dev/null \ - && exec python "$0" "$@" -for pyver in 3 2.7; do - which python$pyver > /dev/null 2>&1 && exec python$pyver "$0" "$@" -done -echo "No appropriate python interpreter found." >&2 -exit 1 -":""" - -from __future__ import division, unicode_literals +from __future__ import division, unicode_literals, print_function import cmd import codecs @@ -48,8 +34,8 @@ from contextlib import contextmanager from glob import glob from uuid import UUID -if sys.version_info.major != 3 and (sys.version_info.major == 2 and sys.version_info.minor != 7): - sys.exit("\nCQL Shell supports only Python 3 or Python 2.7\n") +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") # see CASSANDRA-10428 if platform.python_implementation().startswith('Jython'): @@ -532,6 +518,7 @@ class Shell(cmd.Cmd): if tty: self.reset_prompt() + self.maybe_warn_py2() self.report_connection() print('Use HELP for help.') else: @@ -605,7 +592,7 @@ class Shell(cmd.Cmd): self.show_version() def show_host(self): - print("Connected to {0} at {1}:{2}." + print("Connected to {0} at {1}:{2}" .format(self.applycolor(self.get_cluster_name(), BLUE), self.hostname, self.port)) @@ -618,6 +605,12 @@ class Shell(cmd.Cmd): 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.session, sessionid, partial_session) diff --git a/doc/source/getting_started/installing.rst b/doc/source/getting_started/installing.rst index 5c5239db90..568549f613 100644 --- a/doc/source/getting_started/installing.rst +++ b/doc/source/getting_started/installing.rst @@ -43,7 +43,7 @@ Prerequisites - **NOTE**: *Experimental* support for Java 11 was added in Cassandra 4.0 (`CASSANDRA-9608 `__). Running Cassandra on Java 11 is *experimental*. Do so at your own risk. For more information, see `NEWS.txt `__. -- For using cqlsh, the latest version of `Python 2.7 `__ or Python 3.6+. To verify that you have +- For using cqlsh, the latest version of `Python 3.6+ `__ or Python 2.7 (support deprecated). To verify that you have the correct version of Python installed, type ``python --version``. Choosing an installation method diff --git a/doc/source/tools/cqlsh.rst b/doc/source/tools/cqlsh.rst index b800b88f46..6fc2ee24cd 100644 --- a/doc/source/tools/cqlsh.rst +++ b/doc/source/tools/cqlsh.rst @@ -13,7 +13,7 @@ the Python native protocol driver, and connects to the single node specified on Compatibility ^^^^^^^^^^^^^ -cqlsh is compatible with Python 2.7. +cqlsh is compatible with Python 3.6+ (and 2.7, deprecated). In general, a given version of cqlsh is only guaranteed to work with the version of Cassandra that it was released with. In some cases, cqlsh make work with older or newer versions of Cassandra, but this is not officially supported. diff --git a/pylib/cqlshlib/test/test_cqlsh_output.py b/pylib/cqlshlib/test/test_cqlsh_output.py index 324587d3df..4432a54904 100644 --- a/pylib/cqlshlib/test/test_cqlsh_output.py +++ b/pylib/cqlshlib/test/test_cqlsh_output.py @@ -22,6 +22,7 @@ from __future__ import unicode_literals, with_statement import locale import os import re +import subprocess import sys import unittest @@ -36,6 +37,9 @@ from .ansi_colors import (ColoredText, ansi_seq, lookup_colorcode, CONTROL_C = '\x03' CONTROL_D = '\x04' +has_python27 = not subprocess.call(['python2.7', '--version']) +has_python3 = not subprocess.call(['python3', '--version']) + class TestCqlshOutput(BaseTestCase): @classmethod @@ -777,9 +781,27 @@ class TestCqlshOutput(BaseTestCase): output = c.cmd_and_response('show host;') self.assertHasColors(output) - self.assertRegex(output, '^Connected to .* at %s:%d\.$' + self.assertRegex(output, '^Connected to .* at %s:%d$' % (re.escape(TEST_HOST), TEST_PORT)) + @unittest.skipIf(not has_python27, 'Python 2.7 not available to test warning') + def test_warn_py2(self): + env = self.default_env.copy() + env['USER_SPECIFIED_PYTHON'] = 'python2.7' + # has the warning + with testrun_cqlsh(tty=True, env=env) as c: + self.assertIn('Python 2.7 support is deprecated.', c.output_header, 'cqlsh did not output expected warning.') + + # can suppress + 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(not (has_python27 and has_python3), 'Python 3 and 2.7 not available to test preference') + def test_no_warn_both_py_present(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: