You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by kl...@apache.org on 2018/07/16 15:07:01 UTC

mesos git commit: Updated CLI to Python 3.

Repository: mesos
Updated Branches:
  refs/heads/master b1eafc035 -> da8cac169


Updated CLI to Python 3.

The build tools are also up to date thus the CLI can still be built
using Autotools and CMake. No features have been added to the CLI.

Along the way, we have also fixed some linting errors introduced when
upgrading pylint a few commits ago. They are included in this commit
instead of separated out because the commit chain would not apply
cleanly unless they were committed together.

Review: https://reviews.apache.org/r/67488/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/da8cac16
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/da8cac16
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/da8cac16

Branch: refs/heads/master
Commit: da8cac1691cdb04c179e021980fbfd7773347095
Parents: b1eafc0
Author: Armand Grillet <ag...@mesosphere.io>
Authored: Mon Jul 16 16:39:35 2018 +0200
Committer: Kevin Klues <kl...@gmail.com>
Committed: Mon Jul 16 17:06:27 2018 +0200

----------------------------------------------------------------------
 cmake/CompilationConfigure.cmake                | 75 ++++++++++----------
 configure.ac                                    | 48 +++++++++----
 src/Makefile.am                                 |  2 +-
 src/python/cli_new/CMakeLists.txt               |  6 +-
 src/python/cli_new/README.md                    | 15 ++--
 src/python/cli_new/bin/main.py                  | 12 ++--
 src/python/cli_new/bootstrap                    | 25 ++-----
 src/python/cli_new/lib/cli/config.py            |  2 +-
 src/python/cli_new/lib/cli/docopt.py            |  7 +-
 src/python/cli_new/lib/cli/http.py              | 11 +--
 .../cli_new/lib/cli/plugins/agent/main.py       |  6 +-
 src/python/cli_new/lib/cli/plugins/task/main.py |  6 +-
 src/python/cli_new/lib/cli/tests/base.py        |  8 +--
 src/python/cli_new/lib/cli/tests/tests.py       |  2 +-
 src/python/cli_new/lib/cli/util.py              | 16 ++---
 src/python/cli_new/tests/main.py                |  2 +-
 src/python/cli_new/tox.ini                      | 10 +--
 src/python/lib/tox.ini                          | 10 +--
 support/mesos-style.py                          |  2 +-
 support/python3/mesos-style.py                  |  2 +-
 20 files changed, 135 insertions(+), 132 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/cmake/CompilationConfigure.cmake
----------------------------------------------------------------------
diff --git a/cmake/CompilationConfigure.cmake b/cmake/CompilationConfigure.cmake
index 61387d7..49b2dc0 100644
--- a/cmake/CompilationConfigure.cmake
+++ b/cmake/CompilationConfigure.cmake
@@ -132,53 +132,50 @@ option(
   FALSE)
 
 option(
+  PYTHON
+  "Command for the Python interpreter, set to `python` if not given."
+  "python")
+
+option(
+  PYTHON_3
+  "Command for the Python 3 interpreter, set to the option PYTHON if not given."
+  "")
+
+option(
   ENABLE_NEW_CLI
   "Build the new CLI instead of the old one."
   FALSE)
 
 if (ENABLE_NEW_CLI)
-  find_package(PythonInterp)
-  find_package(PythonLibs)
-
-  if (NOT PYTHON_LIBRARY)
-    message(
-      FATAL_ERROR
-      "Python not found.\n"
-      "The new CLI requires Python version 2.6 or 2.7 in order to build.\n"
-      "Your Python version is ${PYTHONLIBS_VERSION_STRING}.\n"
-      "You may wish to set the PYTHON environment variable to an "
-      "appropriate value if Python is not installed in your PATH.")
+  # We always want to have PYTHON_3 set as it will be used to build the CLI.
+  if (NOT PYTHON_3)
+    if (PYTHON)
+      # Set PYTHON_3 to PYTHON if PYTHON is set but not PYTHON_3.
+      set(PYTHON_3 ${PYTHON})
+    else ()
+      # Set PYTHON_3 to the one CMake finds if PYTHON is not set.
+      # PythonInterp sets PYTHON_EXECUTABLE by looking for an interpreter
+      # from newest to oldest,, we then use it to set PYTHON and PYTHON_3.
+      find_package(PythonInterp)
+      if (NOT PYTHONINTERP_FOUND)
+        message(FATAL_ERROR "You must have Python set up in order to continue.")
+      endif ()
+      set(PYTHON ${PYTHON_EXECUTABLE})
+      set(PYTHON_3 ${PYTHON})
+    endif ()
   endif ()
 
-  if (${PYTHONLIBS_VERSION_STRING} VERSION_LESS "2.6.0")
-    message(
-      FATAL_ERROR
-      "Python version too old.\n"
-      "The new CLI requires Python version 2.6 or 2.7 in order to build.\n"
-      "Your Python version is ${PYTHONLIBS_VERSION_STRING}.\n"
-      "You may wish to set the PYTHON environment variable to an "
-      "appropriate value to assure the right Python executable is found.")
-  endif ()
-
-  if (${PYTHONLIBS_VERSION_STRING} VERSION_EQUAL "3.0.0" OR
-      ${PYTHONLIBS_VERSION_STRING} VERSION_GREATER "3.0.0")
-    message(
-      FATAL_ERROR
-      "Python version too new.\n"
-      "The new CLI requires Python version 2.6 or 2.7 in order to build.\n"
-      "Your Python version is ${PYTHONLIBS_VERSION_STRING}.\n"
-      "You may wish to set the PYTHON environment variable to an "
-      "appropriate value to assure the right Python executable is found.")
-  endif ()
+  execute_process(
+    COMMAND ${PYTHON_3} -c
+      "import sys; print('%d.%d' % (sys.version_info[0], sys.version_info[1]))"
+    OUTPUT_VARIABLE PYTHON_3_VERSION
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
 
-  find_program(VIRTUALENV virtualenv)
-  if (NOT VIRTUALENV)
-    message(
-      FATAL_ERROR
-      "Cannot find virtualenv.\n"
-      "The new CLI requires 'virtualenv' be installed as part of your "
-      "Python ${PYTHONLIBS_VERSION_STRING} installation.\n"
-      "You may wish to install it via 'pip install virtualenv'.")
+  if (PYTHON_3_VERSION VERSION_LESS "3.6.0")
+    message(FATAL_ERROR
+    "You must be running python 3.6 or newer in order to continue.\n"
+    "You appear to be running Python ${PYTHON_3_VERSION}.\n"
+    "Set the CMake option 'PYTHON_3' to define which interpreter to use.")
   endif ()
 endif ()
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 66cc28a..5f64168 100644
--- a/configure.ac
+++ b/configure.ac
@@ -763,6 +763,8 @@ AC_ARG_VAR([PROTOBUF_JAR], [full path to protobuf jar on prefixed builds])
 
 AC_ARG_VAR([PYTHON], [which Python 2 interpreter to use])
 
+AC_ARG_VAR([PYTHON_3], [which Python 3 interpreter to use])
+
 AC_ARG_VAR([TEST_DRIVER], [executable and arguments of a test driver])
 
 # Check for pthreads (uses m4/ax_pthread.m4).
@@ -2338,7 +2340,9 @@ if test "x$enable_python" = "xyes" || \
   if test -z "$PYTHON" && test -z "$PYTHON_VERSION"; then
     AM_PATH_PYTHON()
   fi
+fi
 
+if test "x$enable_python" = "xyes"; then
   # Check if PYTHON is at least 2.6 or try and find one that is if
   # PYTHON is not set (necessary to run our examples).
   AM_PYTHON_CHECK_VERSION([$PYTHON], [2.6], [],
@@ -2362,6 +2366,37 @@ on the path), you might want to check if you have the PYTHON environment
 variable set to a version of Python greater than 3.0.
 -------------------------------------------------------------------
   ])], [])
+
+  if test "x$enable_new_cli" = "xyes" && test -z "$PYTHON_3"; then
+    AC_MSG_ERROR([Missing PYTHON_3 environment variable
+-------------------------------------------------------------------
+The detected Python version is $PYTHON_VERSION. Python 3 not detected.
+
+When building both the Python bindings and the CLI, the detected Python
+version is used to build the bindings and PYTHON_3 must be set
+explicitely in order to build the CLI.
+-------------------------------------------------------------------
+    ])
+  fi
+fi
+
+if test "x$enable_new_cli" = "xyes"; then
+  if test -z "$PYTHON_3"; then
+    AC_SUBST([PYTHON_3], [$PYTHON])
+  fi
+
+  PYTHON_3_VERSION=`$PYTHON_3 -c 'import sys; print(sys.version[[0:3]])'`
+
+  AM_PYTHON_CHECK_VERSION([$PYTHON_3], [3.6], [],
+                 [AC_MSG_ERROR([The Mesos CLI requires Python >= 3.6
+-------------------------------------------------------------------
+The detected Python 3 version is $PYTHON_3_VERSION.
+
+The new CLI requires Python version 3.6 or newer in order to build.
+You may wish to set the PYTHON_3 environment variable to make sure
+that the right Python executable is found.
+-------------------------------------------------------------------
+  ])])
 fi
 
 if test "x$enable_python" = "xyes"; then
@@ -2643,19 +2678,6 @@ AS_IF([test "x$enable_new_cli" = "xyes"],
       [AC_MSG_RESULT([yes])],
       [AC_MSG_RESULT([no])])
 
-AS_IF([test "x$enable_new_cli" = "xyes"], [
-  AC_CHECK_PROG([VIRTUALENV_CHECK], [virtualenv], [yes])
-  AS_IF([test "x$VIRTUALENV_CHECK" = "xyes"],, [
-     AC_MSG_ERROR([Cannot find virtualenv
--------------------------------------------------------------------
-The new CLI requires 'virtualenv' be installed as part of your
-Python $PYTHON_VERSION installation.
-
-You may wish to install it via 'pip install virtualenv'.
--------------------------------------------------------------------
-  ])])
-])
-
 AM_CONDITIONAL([ENABLE_NEW_CLI], [test "x$enable_new_cli" = "xyes"])
 
 AM_CONDITIONAL([HAS_PYTHON], [test "x$has_python" = "xyes"])

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 9c13eaa..228e168 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1772,7 +1772,7 @@ MESOS_CLI_HIDDEN_IMPORTS =					\
 mesos: $(shell find $(MESOS_CLI_SRCDIR) | grep -v .virtualenv)
 	@echo "Building the CLI ..."
 	VIRTUALENV_DIRECTORY=$(builddir)/.virtualenv	\
-	PYTHON=$(PYTHON)				\
+	PYTHON=$(PYTHON_3)				\
 	$(MESOS_CLI_SRCDIR)/bootstrap &&		\
 	echo "VERSION = \"$(PACKAGE_VERSION)\""		\
 		> $(MESOS_CLI_BUILDDIR)/version.py &&	\

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/python/cli_new/CMakeLists.txt b/src/python/cli_new/CMakeLists.txt
index ef8da70..9eda917 100644
--- a/src/python/cli_new/CMakeLists.txt
+++ b/src/python/cli_new/CMakeLists.txt
@@ -51,7 +51,9 @@ if (ENABLE_NEW_CLI)
   # Creating the virtual environment required by the CLI.
   add_custom_command(
     OUTPUT .virtualenv/bin/activate
-    COMMAND VIRTUALENV_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/.virtualenv
+    COMMAND
+      VIRTUALENV_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/.virtualenv
+      PYTHON=${PYTHON_3}
       ${CMAKE_CURRENT_SOURCE_DIR}/bootstrap
     DEPENDS bootstrap pip-requirements.txt)
 
@@ -68,7 +70,7 @@ if (ENABLE_NEW_CLI)
     echo \"VERSION = \\\"${PACKAGE_VERSION}\\\"\"                           \
       > ${CMAKE_BINARY_DIR}/src/cli/version.py
     source .virtualenv/bin/activate
-    python -m PyInstaller -p ${CMAKE_CURRENT_SOURCE_DIR}/lib                \
+    ${PYTHON_3} -m PyInstaller -p ${CMAKE_CURRENT_SOURCE_DIR}/lib           \
       $MESOS_CLI_HIDDEN_IMPORTS --specpath ${CMAKE_BINARY_DIR}/src/cli      \
       --workpath ${CMAKE_BINARY_DIR}/src/cli/work                           \
       --distpath ${CMAKE_BINARY_DIR}/src\ --name mesos --noconfirm --onefile\

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/README.md
----------------------------------------------------------------------
diff --git a/src/python/cli_new/README.md b/src/python/cli_new/README.md
index 3d646e9..0e6c716 100644
--- a/src/python/cli_new/README.md
+++ b/src/python/cli_new/README.md
@@ -2,19 +2,14 @@
 
 ## Prerequisites
 
-Make sure you have the following prerequisites
-installed on your system before you begin.
-
-```
-python 2.6 or 2.7
-virtualenv
-```
+Make sure you have python 3.6 or newer installed
+on your system before you begin.
 
 ## Getting Started
 
-Once you you have the prerequisites installed, simply
-run the `bootstrap` script from this directory to set
-up a python virtual environment and start running the tool.
+Once you you have the prerequisites installed, simply run the
+`bootstrap` script from this directory to set up a virtual
+environment using python 3 and start running the tool.
 
 ```
 $ ./bootstrap

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/bin/main.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/bin/main.py b/src/python/cli_new/bin/main.py
index 5313038..b6f9fab 100644
--- a/src/python/cli_new/bin/main.py
+++ b/src/python/cli_new/bin/main.py
@@ -62,7 +62,7 @@ def autocomplete(cmds, plugins, config, argv):
     current_word = argv[0]
     argv = argv[1:]
 
-    if len(argv) > 0 and argv[0] == "help":
+    if argv and argv[0] == "help":
         argv = argv[1:]
 
     comp_words = list(cmds.keys()) + ["help"]
@@ -93,7 +93,7 @@ def main(argv):
     cmds = {
         cli.util.get_module(plugins, plugin).PLUGIN_NAME:
         cli.util.get_module(plugins, plugin).SHORT_HELP
-        for plugin in plugins.keys()
+        for plugin in list(plugins.keys())
     }
 
     # Parse all incoming arguments using docopt.
@@ -122,13 +122,13 @@ def main(argv):
         except Exception:
             pass
 
-        print option
-        print " ".join(comp_words)
+        print(option)
+        print(" ".join(comp_words))
 
     # Use the meta-command "help" to print help information for the
     # supplied command and its subcommands.
     elif cmd == "help":
-        if len(argv) > 0 and argv[0] in cmds:
+        if argv and argv[0] in cmds:
             plugin = cli.util.get_module(plugins, argv[0])
             plugin_class = getattr(plugin, plugin.PLUGIN_CLASS)
             plugin_class(settings, config).main(argv[1:] + ["--help"])
@@ -136,7 +136,7 @@ def main(argv):
             main(["--help"])
 
     # Run the command through its plugin.
-    elif cmd in cmds.keys():
+    elif cmd in list(cmds.keys()):
         plugin = cli.util.get_module(plugins, cmd)
         plugin_class = getattr(plugin, plugin.PLUGIN_CLASS)
         plugin_class(settings, config).main(argv)

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/bootstrap
----------------------------------------------------------------------
diff --git a/src/python/cli_new/bootstrap b/src/python/cli_new/bootstrap
index fb6fbc4..a6183d4 100755
--- a/src/python/cli_new/bootstrap
+++ b/src/python/cli_new/bootstrap
@@ -30,36 +30,21 @@ fi
 
 # Verify that python and virtualenv are installed.
 if [ "${PYTHON}" = "" ]; then
-  echo "You must have python installed in order to continue..."
+  echo "You must have python installed in order to continue."
   exit 1
 fi
 
-if [ "${VIRTUALENV}" = "" ]; then
-  # Search for a locally installed virtualenv.
-  # See https://docs.python.org/2/library/site.html#site.USER_SITE for details.
-  VIRTUALENV=$(${PYTHON} -c "import site; print site.USER_SITE")/virtualenv.py
-
-  if [ ! -f "${VIRTUALENV}" ]; then
-    echo "You must have virtualenv installed in order to continue..."
-    exit 1
-  fi
-fi
-
 PYTHON_MAJOR=$(${PYTHON} -c 'import sys; print(sys.version_info[0])')
 PYTHON_MINOR=$(${PYTHON} -c 'import sys; print(sys.version_info[1])')
 
-if [ "${PYTHON_MAJOR}" != "2" ] || [ "${PYTHON_MINOR}" -lt "6" ]; then
-  echo "You must be running python 2.6 or 2.7 in order to continue."
-  echo "Consider running as 'PYTHON=python2 ./bootstrap' or similar."
+if [ "${PYTHON_MAJOR}" != "3" ] || [ "${PYTHON_MINOR}" -lt "6" ]; then
+  echo "You must be running python 3.6 or newer in order to continue."
+  echo "Consider running as 'PYTHON=python3 ./bootstrap' or similar."
   exit 1
 fi
 
 # Set up a virtual environment for the CLI.
-${PYTHON} ${VIRTUALENV} --python=${PYTHON} \
-                        --clear \
-                        --no-site-packages \
-                        --prompt="(${VIRTUALENV_NAME}) " \
-                        ${VIRTUALENV_DIRECTORY} || true
+${PYTHON} -m venv --clear --prompt="${VIRTUALENV_NAME}" ${VIRTUALENV_DIRECTORY}
 
 source ${VIRTUALENV_DIRECTORY}/bin/activate
 SITE_PACKAGES=$(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/config.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/config.py b/src/python/cli_new/lib/cli/config.py
index 6f92622..92ff3eb 100644
--- a/src/python/cli_new/lib/cli/config.py
+++ b/src/python/cli_new/lib/cli/config.py
@@ -91,7 +91,7 @@ class Config(object):
                                        " an 'addresses' list")
 
                 if ("path" not in zk_field or
-                        not isinstance(zk_field["path"], unicode)):
+                        not isinstance(zk_field["path"], str)):
                     raise CLIException("The 'zookeeper' field must contain"
                                        " a 'path' field")
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/docopt.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/docopt.py b/src/python/cli_new/lib/cli/docopt.py
index 86a4e9c..4dedd77 100644
--- a/src/python/cli_new/lib/cli/docopt.py
+++ b/src/python/cli_new/lib/cli/docopt.py
@@ -46,7 +46,6 @@ Hopefully we can remove this brutal hack at some point in the future
 once docopt supports the "program" argument natively.
 """
 
-from __future__ import absolute_import
 import os
 import sys
 
@@ -74,7 +73,7 @@ def docopt(usage, **keywords):
 
     except DocoptExit:
         sys.stdout = stdout
-        print >> sys.stderr, usage.strip()
+        print(usage.strip(), file=sys.stderr)
         sys.exit(1)
 
     except SystemExit:
@@ -82,9 +81,9 @@ def docopt(usage, **keywords):
 
         if "argv" in keywords and any(h in ("-h", "--help")
                                       for h in keywords["argv"]):
-            print usage.strip()
+            print(usage.strip())
         elif "version" in keywords and any(v in ("--version")
                                            for v in keywords["argv"]):
-            print keywords["version"].strip()
+            print(keywords["version"].strip())
 
         sys.exit()

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/http.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/http.py b/src/python/cli_new/lib/cli/http.py
index 03d6031..d1faac1 100644
--- a/src/python/cli_new/lib/cli/http.py
+++ b/src/python/cli_new/lib/cli/http.py
@@ -19,7 +19,9 @@ A collection of http related functions used by the CLI and its Plugins.
 """
 
 import json
-import urllib2
+import urllib.request
+import urllib.error
+import urllib.parse
 import time
 
 from cli.exceptions import CLIException
@@ -32,7 +34,7 @@ def read_endpoint(addr, endpoint):
     try:
         url = "http://{addr}/{endpoint}".format(addr=addr, endpoint=endpoint)
 
-        http_response = urllib2.urlopen(url).read().decode("utf-8")
+        http_response = urllib.request.urlopen(url).read().decode("utf-8")
     except Exception as exception:
         raise CLIException("{error}".format(error=str(exception)))
 
@@ -70,8 +72,7 @@ def get_json(addr, endpoint, condition=None, timeout=5):
                 return data
 
         if time.time() - start_time > timeout:
-            raise CLIException("Failed to get data within {seconds} seconds:"
-                               " {error}"
-                               .format(seconds=str(timeout), error=exception))
+            raise CLIException("Failed to get data within {seconds} seconds"
+                               .format(seconds=str(timeout)))
 
         time.sleep(0.1)

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/plugins/agent/main.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/plugins/agent/main.py b/src/python/cli_new/lib/cli/plugins/agent/main.py
index 59280ec..4ebf8cd 100644
--- a/src/python/cli_new/lib/cli/plugins/agent/main.py
+++ b/src/python/cli_new/lib/cli/plugins/agent/main.py
@@ -65,8 +65,8 @@ class Agent(PluginBase):
                                " endpoint at '{addr}': {error}"
                                .format(addr=master, error=exception))
 
-        if len(agents) == 0:
-            print "The cluster does not have any agents."
+        if not agents:
+            print("The cluster does not have any agents.")
             return
 
         try:
@@ -79,4 +79,4 @@ class Agent(PluginBase):
             raise CLIException("Unable to build table of agents: {error}"
                                .format(error=exception))
 
-        print str(table)
+        print(str(table))

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/plugins/task/main.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/plugins/task/main.py b/src/python/cli_new/lib/cli/plugins/task/main.py
index cc6cff5..f9d6826 100644
--- a/src/python/cli_new/lib/cli/plugins/task/main.py
+++ b/src/python/cli_new/lib/cli/plugins/task/main.py
@@ -64,8 +64,8 @@ class Task(PluginBase):
                                " endpoint at '{addr}': {error}"
                                .format(addr=master, error=exception))
 
-        if len(tasks) == 0:
-            print "There are no tasks running in the cluster."
+        if not tasks:
+            print("There are no tasks running in the cluster.")
             return
 
         try:
@@ -78,4 +78,4 @@ class Task(PluginBase):
             raise CLIException("Unable to build table of tasks: {error}"
                                .format(error=exception))
 
-        print str(table)
+        print(str(table))

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/tests/base.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/tests/base.py b/src/python/cli_new/lib/cli/tests/base.py
index 89360e6..f58d616 100644
--- a/src/python/cli_new/lib/cli/tests/base.py
+++ b/src/python/cli_new/lib/cli/tests/base.py
@@ -18,9 +18,9 @@
 Set of classes and helper functions for building unit tests for the Mesos CLI.
 """
 
+import io
 import os
 import shutil
-import StringIO
 import subprocess
 import sys
 import tempfile
@@ -49,7 +49,7 @@ class CLITestCase(unittest.TestCase):
 
     @classmethod
     def setUpClass(cls):
-        print "\n{class_name}".format(class_name=cls.__name__)
+        print("\n{class_name}".format(class_name=cls.__name__))
 
     @staticmethod
     def default_mesos_build_dir():
@@ -109,7 +109,7 @@ class Executable(object):
 
         try:
             flags = ["--{key}={value}".format(key=key, value=value)
-                     for key, value in self.flags.iteritems()]
+                     for key, value in dict(self.flags).items()]
 
             if self.shell:
                 cmd = ["/bin/sh", self.executable] + flags
@@ -431,7 +431,7 @@ def capture_output(command, argv, extra_args=None):
         extra_args = {}
 
     stdout = sys.stdout
-    sys.stdout = StringIO.StringIO()
+    sys.stdout = io.StringIO()
 
     try:
         command(argv, **extra_args)

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/tests/tests.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/tests/tests.py b/src/python/cli_new/lib/cli/tests/tests.py
index 79e1036..60aa4e8 100644
--- a/src/python/cli_new/lib/cli/tests/tests.py
+++ b/src/python/cli_new/lib/cli/tests/tests.py
@@ -58,7 +58,7 @@ class TestInfrastructure(CLITestCase):
 
         # pylint: disable=missing-docstring
         def command(args):
-            print template.format(args=args)
+            print(template.format(args=args))
 
         output = capture_output(command, arguments)
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/lib/cli/util.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/lib/cli/util.py b/src/python/cli_new/lib/cli/util.py
index 307b222..b740431 100644
--- a/src/python/cli_new/lib/cli/util.py
+++ b/src/python/cli_new/lib/cli/util.py
@@ -26,10 +26,11 @@ import re
 import socket
 import textwrap
 
+from kazoo.client import KazooClient
+
 import cli.http as http
 
 from cli.exceptions import CLIException
-from kazoo.client import KazooClient
 
 
 def import_modules(package_paths, module_type):
@@ -89,7 +90,7 @@ def completions(comp_words, current_word, argv):
     """
     comp_words += ["-h", "--help", "--version"]
 
-    if len(argv) == 0:
+    if not argv:
         return comp_words
 
     if len(argv) == 1:
@@ -118,7 +119,7 @@ def format_commands_help(cmds):
     """
     Helps format plugin commands for display.
     """
-    longest_cmd_name = max(cmds.keys(), key=len)
+    longest_cmd_name = max(list(cmds.keys()), key=len)
 
     help_string = ""
     for cmd in sorted(cmds.keys()):
@@ -148,8 +149,8 @@ def format_subcommands_help(cmd):
     flags["-h --help"] = "Show this screen."
     flag_string = ""
 
-    if len(flags.keys()) != 0:
-        longest_flag_name = max(flags.keys(), key=len)
+    if list(flags.keys()) != 0:
+        longest_flag_name = max(list(flags.keys()), key=len)
         for flag in sorted(flags.keys()):
             num_spaces = len(longest_flag_name) - len(flag) + 2
             flag_string += "  %s%s%s\n" % (flag, " " * num_spaces, flags[flag])
@@ -161,10 +162,7 @@ def verify_address_format(address):
     """
     Verify that an address ip and port are correct.
     """
-    # We use 'basestring' as the type of address because it can be
-    # 'str' or 'unicode' depending on the source of the address (e.g.
-    # a config file or a flag). Both types inherit from basestring.
-    if not isinstance(address, basestring):
+    if not isinstance(address, str):
         raise CLIException("The address must be a string")
 
     address_pattern = re.compile(r'[0-9]+(?:\.[0-9]+){3}:[0-9]+')

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/tests/main.py
----------------------------------------------------------------------
diff --git a/src/python/cli_new/tests/main.py b/src/python/cli_new/tests/main.py
index acf2e08..ef02fdd 100644
--- a/src/python/cli_new/tests/main.py
+++ b/src/python/cli_new/tests/main.py
@@ -36,5 +36,5 @@ if __name__ == '__main__':
     os.environ["MESOS_CLI_CONFIG"] = os.path.join(os.path.dirname(__file__),
                                                   "default_config.toml")
 
-    print colored("Running the Mesos CLI unit tests", "yellow")
+    print(colored("Running the Mesos CLI unit tests", "yellow"))
     unittest.main(verbosity=2, testRunner=unittest.TextTestRunner)

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/cli_new/tox.ini
----------------------------------------------------------------------
diff --git a/src/python/cli_new/tox.ini b/src/python/cli_new/tox.ini
index 58ca380..a9a2e69 100644
--- a/src/python/cli_new/tox.ini
+++ b/src/python/cli_new/tox.ini
@@ -4,19 +4,21 @@
 # and then run "tox" from this directory.
 
 [tox]
-envlist = {py27}-{lint,test}
+envlist = {py3,py36,py37}-{lint,test}
 skipsdist = true
 
 [testenv]
 basepython =
-    py27: python2.7
+    py3: python3
+    py36: python3.6
+    py37: python3.7
 deps =
     -rpip-requirements.txt
     test: coverage
           mock
           pytest
           pytest-cov
-    lint: pylint==1.6.4
+    lint: pylint==1.9.2
 commands =
     test: py.test {posargs:tests -vv --cov=lib --cov=bin --cov-report=term-missing}
-    lint: pylint --rcfile=../../../support/pylint.config {posargs:lib/cli tests}
+    lint: pylint --score=n --rcfile=../../../support/pylint.config {posargs:lib/cli tests}

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/src/python/lib/tox.ini
----------------------------------------------------------------------
diff --git a/src/python/lib/tox.ini b/src/python/lib/tox.ini
index 05b633e..0779fb2 100644
--- a/src/python/lib/tox.ini
+++ b/src/python/lib/tox.ini
@@ -4,19 +4,21 @@
 # and then run "tox" from this directory.
 
 [tox]
-envlist = {py27}-{lint,test}
+envlist = {py3,py36,py37}-{lint,test}
 skipsdist = true
 
 [testenv]
 basepython =
-    py27: python2.7
+    py3: python3
+    py36: python3.6
+    py37: python3.7
 deps =
     -rrequirements.in
     test: coverage
           mock
           pytest
           pytest-cov
-    lint: pylint==1.6.4
+    lint: pylint==1.9.2
 commands =
     test: py.test {posargs:tests -vv --cov=mesos --cov-report=term-missing}
-    lint: pylint --rcfile=../../../support/pylint.config {posargs:mesos tests setup.py}
+    lint: pylint --score=n --rcfile=../../../support/pylint.config {posargs:mesos tests setup.py}

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/support/mesos-style.py
----------------------------------------------------------------------
diff --git a/support/mesos-style.py b/support/mesos-style.py
index 32a5f17..6726e6e 100755
--- a/support/mesos-style.py
+++ b/support/mesos-style.py
@@ -417,7 +417,7 @@ class PyLinter(LinterBase):
             process = self.run_tox(
                 configfile=os.path.join(source_dir, 'tox.ini'),
                 args=['--rcfile='+self.pylint_config] + filtered_source_files,
-                tox_env='py27-lint')
+                tox_env='py3-lint')
         else:
             process = self.run_command_in_virtualenv(
                 'pylint --score=n --rcfile={rcfile} {files}'.format(

http://git-wip-us.apache.org/repos/asf/mesos/blob/da8cac16/support/python3/mesos-style.py
----------------------------------------------------------------------
diff --git a/support/python3/mesos-style.py b/support/python3/mesos-style.py
index b15da95..cba47c3 100755
--- a/support/python3/mesos-style.py
+++ b/support/python3/mesos-style.py
@@ -420,7 +420,7 @@ class PyLinter(LinterBase):
             process = self.run_tox(
                 configfile=os.path.join(source_dir, 'tox.ini'),
                 args=['--rcfile='+self.pylint_config] + filtered_source_files,
-                tox_env='py27-lint')
+                tox_env='py3-lint')
         else:
             process = self.run_command_in_virtualenv(
                 'pylint --score=n --rcfile={rcfile} {files}'.format(