remove more Python2 compatibility
diff --git a/compiler/cpp/test/compiler/staleness_check.py b/compiler/cpp/test/compiler/staleness_check.py
index 6b67798..4dcec15 100755
--- a/compiler/cpp/test/compiler/staleness_check.py
+++ b/compiler/cpp/test/compiler/staleness_check.py
@@ -17,7 +17,6 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-from __future__ import print_function
 import os
 import shutil
 import subprocess
diff --git a/contrib/fb303/py/fb303_scripts/fb303_simple_mgmt.py b/contrib/fb303/py/fb303_scripts/fb303_simple_mgmt.py
index 5c8f409..62a729e 100644
--- a/contrib/fb303/py/fb303_scripts/fb303_simple_mgmt.py
+++ b/contrib/fb303/py/fb303_scripts/fb303_simple_mgmt.py
@@ -19,7 +19,6 @@
 # under the License.
 #
 
-from __future__ import print_function
 import sys
 import os
 from optparse import OptionParser
diff --git a/lib/java/src/test/java/org/apache/thrift/test/EqualityTest.java b/lib/java/src/test/java/org/apache/thrift/test/EqualityTest.java
index fee495c..02a14d0 100644
--- a/lib/java/src/test/java/org/apache/thrift/test/EqualityTest.java
+++ b/lib/java/src/test/java/org/apache/thrift/test/EqualityTest.java
@@ -20,10 +20,7 @@
 /*
 This program was generated by the following Python script:
 
-#!/usr/bin/python2.5
-
-# Remove this when Python 2.6 hits the streets.
-from __future__ import with_statement
+#!/usr/bin/python3
 
 import sys
 import os.path
diff --git a/lib/py/setup.py b/lib/py/setup.py
index 066483e..2a170a4 100644
--- a/lib/py/setup.py
+++ b/lib/py/setup.py
@@ -97,8 +97,6 @@
         extensions = dict()
 
     ssl_deps = []
-    if sys.version_info[0] == 2:
-        ssl_deps.append('ipaddress')
     if sys.hexversion < 0x03050000:
         ssl_deps.append('backports.ssl_match_hostname>=3.5')
     tornado_deps = ['tornado>=4.0']
diff --git a/lib/py/src/TRecursive.py b/lib/py/src/TRecursive.py
index abf202c..aed696a 100644
--- a/lib/py/src/TRecursive.py
+++ b/lib/py/src/TRecursive.py
@@ -10,11 +10,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
 from thrift.Thrift import TType
 
 TYPE_IDX = 1
diff --git a/lib/py/src/TTornado.py b/lib/py/src/TTornado.py
index 5eff11d..c409e09 100644
--- a/lib/py/src/TTornado.py
+++ b/lib/py/src/TTornado.py
@@ -17,7 +17,6 @@
 # under the License.
 #
 
-from __future__ import absolute_import
 import logging
 import socket
 import struct
@@ -34,7 +33,7 @@
 logger = logging.getLogger(__name__)
 
 
-class _Lock(object):
+class _Lock:
     def __init__(self):
         self._waiters = deque()
 
diff --git a/lib/py/src/compat.py b/lib/py/src/compat.py
index 0e8271d..3b3d57f 100644
--- a/lib/py/src/compat.py
+++ b/lib/py/src/compat.py
@@ -17,30 +17,13 @@
 # under the License.
 #
 
-import sys
+from io import BytesIO as BufferIO  # noqa
 
-if sys.version_info[0] == 2:
+def binary_to_str(bin_val):
+    return bin_val.decode('utf8')
 
-    from cStringIO import StringIO as BufferIO
+def str_to_binary(str_val):
+    return bytes(str_val, 'utf8')
 
-    def binary_to_str(bin_val):
-        return bin_val
-
-    def str_to_binary(str_val):
-        return str_val
-
-    def byte_index(bytes_val, i):
-        return ord(bytes_val[i])
-
-else:
-
-    from io import BytesIO as BufferIO  # noqa
-
-    def binary_to_str(bin_val):
-        return bin_val.decode('utf8')
-
-    def str_to_binary(str_val):
-        return bytes(str_val, 'utf8')
-
-    def byte_index(bytes_val, i):
-        return bytes_val[i]
+def byte_index(bytes_val, i):
+    return bytes_val[i]
diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py
index 1741702..fef0cc9 100644
--- a/lib/py/src/protocol/TJSONProtocol.py
+++ b/lib/py/src/protocol/TJSONProtocol.py
@@ -263,19 +263,11 @@
 
     def _toChar(self, high, low=None):
         if not low:
-            if sys.version_info[0] == 2:
-                return ("\\u%04x" % high).decode('unicode-escape') \
-                                         .encode('utf-8')
-            else:
-                return chr(high)
+            return chr(high)
         else:
             codepoint = (1 << 16) + ((high & 0x3ff) << 10)
             codepoint += low & 0x3ff
-            if sys.version_info[0] == 2:
-                s = "\\U%08x" % codepoint
-                return s.decode('unicode-escape').encode('utf-8')
-            else:
-                return chr(codepoint)
+            return chr(codepoint)
 
     def readJSONString(self, skipContext):
         highSurrogate = None
@@ -317,7 +309,7 @@
             elif character in ESCAPE_CHAR_VALS:
                 raise TProtocolException(TProtocolException.INVALID_DATA,
                                          "Unescaped control char")
-            elif sys.version_info[0] > 2:
+            else:
                 utf8_bytes = bytearray([ord(character)])
                 while ord(self.reader.peek()) >= 0x80:
                     utf8_bytes.append(ord(self.reader.read()))
diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py
index bac54ae..ec71ab3 100644
--- a/lib/py/src/protocol/TProtocol.py
+++ b/lib/py/src/protocol/TProtocol.py
@@ -261,11 +261,6 @@
                 raise TProtocolException(type=TProtocolException.INVALID_DATA,
                                          message='Invalid binary field type %d' % ttype)
             return ('readBinary', 'writeBinary', False)
-        if sys.version_info[0] == 2 and spec == 'UTF8':
-            if ttype != TType.STRING:
-                raise TProtocolException(type=TProtocolException.INVALID_DATA,
-                                         message='Invalid string field type %d' % ttype)
-            return ('readUtf8', 'writeUtf8', False)
         return self._TTYPE_HANDLERS[ttype] if ttype < len(self._TTYPE_HANDLERS) else (None, None, False)
 
     def _read_by_ttype(self, ttype, spec, espec):
diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py
index ff20d7e..a686b12 100644
--- a/lib/py/src/transport/TTransport.py
+++ b/lib/py/src/transport/TTransport.py
@@ -17,9 +17,10 @@
 # under the License.
 #
 
+from io import BytesIO as BufferIO
+
 from struct import pack, unpack
 from thrift.Thrift import TException
-from ..compat import BufferIO
 
 
 class TTransportException(TException):
diff --git a/lib/py/src/transport/TZlibTransport.py b/lib/py/src/transport/TZlibTransport.py
index e848579..8b08297 100644
--- a/lib/py/src/transport/TZlibTransport.py
+++ b/lib/py/src/transport/TZlibTransport.py
@@ -22,13 +22,12 @@
 data compression.
 """
 
-from __future__ import division
 import zlib
 from .TTransport import TTransportBase, CReadableTransport
 from ..compat import BufferIO
 
 
-class TZlibTransportFactory(object):
+class TZlibTransportFactory:
     """Factory transport that builds zlib compressed transports.
 
     This factory caches the last single client/transport that it was passed
diff --git a/lib/py/test/thrift_json.py b/lib/py/test/thrift_json.py
index 125ea59..5a491e2 100644
--- a/lib/py/test/thrift_json.py
+++ b/lib/py/test/thrift_json.py
@@ -42,8 +42,6 @@
         transport = TTransport.TBufferedTransportFactory().getTransport(buf)
         protocol = TJSONProtocol(transport)
 
-        if sys.version_info[0] == 2:
-            unicode_text = unicode_text.encode('utf8')
         self.assertEqual(protocol.readString(), unicode_text)
 
     def test_TJSONProtocol_write(self):
diff --git a/test/crossrunner/compat.py b/test/crossrunner/compat.py
index 932a48c..a670c33 100644
--- a/test/crossrunner/compat.py
+++ b/test/crossrunner/compat.py
@@ -1,24 +1,7 @@
 import os
-import sys
 
-if sys.version_info[0] == 2:
-    _ENCODE = sys.getfilesystemencoding()
+path_join = os.path.join
+str_join = str.join
 
-    def path_join(*args):
-        bin_args = map(lambda a: a.decode(_ENCODE), args)
-        return os.path.join(*bin_args).encode(_ENCODE)
-
-    def str_join(left, right):
-        bin_args = map(lambda a: a.decode(_ENCODE), right)
-        b = left.decode(_ENCODE)
-        return b.join(bin_args).encode(_ENCODE)
-
-    logfile_open = open
-
-else:
-
-    path_join = os.path.join
-    str_join = str.join
-
-    def logfile_open(*args):
-        return open(*args, errors='replace')
+def logfile_open(*args):
+    return open(*args, errors='replace')
diff --git a/test/crossrunner/report.py b/test/crossrunner/report.py
index 5baf161..7e1b0c7 100644
--- a/test/crossrunner/report.py
+++ b/test/crossrunner/report.py
@@ -17,7 +17,6 @@
 # under the License.
 #
 
-from __future__ import print_function
 import datetime
 import json
 import multiprocessing
@@ -317,10 +316,7 @@
             self._print_bar()
 
     def _http_server_command(self, port):
-        if sys.version_info[0] < 3:
-            return 'python -m SimpleHTTPServer %d' % port
-        else:
-            return 'python -m http.server %d' % port
+        return 'python -m http.server %d' % port
 
     def _print_footer(self):
         fail_count = len(self._expected_failure) + len(self._unexpected_failure)
diff --git a/test/py/FastbinaryTest.py b/test/py/FastbinaryTest.py
index 05c0bb6..f680357 100755
--- a/test/py/FastbinaryTest.py
+++ b/test/py/FastbinaryTest.py
@@ -25,8 +25,6 @@
 
 # TODO(dreiss): Test error cases.  Check for memory leaks.
 
-from __future__ import print_function
-
 import math
 import os
 import sys
@@ -68,15 +66,11 @@
 ooe2.integer64 = 64
 ooe2.double_precision = (math.sqrt(5) + 1) / 2
 ooe2.some_characters = ":R (me going \"rrrr\")"
-ooe2.zomg_unicode = u"\xd3\x80\xe2\x85\xae\xce\x9d\x20"\
-                    u"\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\
-                    u"\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\
-                    u"\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\
-                    u"\xc7\x83\xe2\x80\xbc"
-
-if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
-    ooe1.zomg_unicode = ooe1.zomg_unicode.encode('utf8')
-    ooe2.zomg_unicode = ooe2.zomg_unicode.encode('utf8')
+ooe2.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20"\
+                    "\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\
+                    "\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\
+                    "\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\
+                    "\xc7\x83\xe2\x80\xbc"
 
 hm = HolyMoley(**{"big": [], "contain": set(), "bonks": {}})
 hm.big.append(ooe1)
@@ -86,10 +80,7 @@
 
 hm.contain.add(("and a one", "and a two"))
 hm.contain.add(("then a one, two", "three!", "FOUR!"))
-if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
-    hm.contain.add((u"\xd7\n\a\t".encode('utf8'),))
-else:
-    hm.contain.add((u"\xd7\n\a\t",))
+hm.contain.add(("\xd7\n\a\t",))
 hm.contain.add(())
 
 hm.bonks["nothing"] = []
diff --git a/test/py/RunClientServer.py b/test/py/RunClientServer.py
index 583d88a..f48224f 100755
--- a/test/py/RunClientServer.py
+++ b/test/py/RunClientServer.py
@@ -19,8 +19,6 @@
 # under the License.
 #
 
-from __future__ import division
-from __future__ import print_function
 import platform
 import copy
 import os
diff --git a/test/py/SerializationTest.py b/test/py/SerializationTest.py
index f47c3d4..218f26c 100755
--- a/test/py/SerializationTest.py
+++ b/test/py/SerializationTest.py
@@ -278,9 +278,6 @@
         self.assertTrue(len(rep) > 0)
 
     def testIntegerLimits(self):
-        if (sys.version_info[0] == 2 and sys.version_info[1] <= 6):
-            print('Skipping testIntegerLimits for Python 2.6')
-            return
         bad_values = [CompactProtoTestStruct(a_byte=128), CompactProtoTestStruct(a_byte=-129),
                       CompactProtoTestStruct(a_i16=32768), CompactProtoTestStruct(a_i16=-32769),
                       CompactProtoTestStruct(a_i32=2147483648), CompactProtoTestStruct(a_i32=-2147483649),
diff --git a/test/py/TestClient.py b/test/py/TestClient.py
index 61a9c60..d80ddf4 100755
--- a/test/py/TestClient.py
+++ b/test/py/TestClient.py
@@ -106,9 +106,6 @@
         Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
         Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
         Bân-lâm-gú, 粵語"""
-        if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
-            s1 = s1.encode('utf8')
-            s2 = s2.encode('utf8')
         self.assertEqual(self.client.testString(s1), s1)
         self.assertEqual(self.client.testString(s2), s2)
 
diff --git a/test/py/TestServer.py b/test/py/TestServer.py
index 81ae1ad..e062378 100755
--- a/test/py/TestServer.py
+++ b/test/py/TestServer.py
@@ -18,7 +18,6 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-from __future__ import division
 import logging
 import os
 import signal
diff --git a/test/test.py b/test/test.py
index cd5c4d4..a288c26 100755
--- a/test/test.py
+++ b/test/test.py
@@ -27,7 +27,6 @@
 # subprocess management that are needed for reliability.
 #
 
-from __future__ import print_function
 from itertools import chain
 import json
 import logging