You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by ns...@apache.org on 2015/11/06 13:26:53 UTC

[1/4] thrift git commit: THRIFT-1857 Python 3 Support Client: Python Patch: Thomas Bartelmess, Eevee (Alex Munroe), helgridly, Christian Verkerk, Jeroen Vlek, Nobuaki Sukegawa

Repository: thrift
Updated Branches:
  refs/heads/master 49f4dc0cd -> 8a4d06feb


http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py/PythonServer.py
----------------------------------------------------------------------
diff --git a/tutorial/py/PythonServer.py b/tutorial/py/PythonServer.py
index 533b0ea..8ef231b 100755
--- a/tutorial/py/PythonServer.py
+++ b/tutorial/py/PythonServer.py
@@ -21,7 +21,7 @@
 
 import sys, glob
 sys.path.append('gen-py')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from tutorial import Calculator
 from tutorial.ttypes import *
@@ -38,14 +38,14 @@ class CalculatorHandler:
     self.log = {}
 
   def ping(self):
-    print 'ping()'
+    print('ping()')
 
   def add(self, n1, n2):
-    print 'add(%d,%d)' % (n1, n2)
+    print('add(%d,%d)' % (n1, n2))
     return n1+n2
 
   def calculate(self, logid, work):
-    print 'calculate(%d, %r)' % (logid, work)
+    print('calculate(%d, %r)' % (logid, work))
 
     if work.op == Operation.ADD:
       val = work.num1 + work.num2
@@ -74,11 +74,11 @@ class CalculatorHandler:
     return val
 
   def getStruct(self, key):
-    print 'getStruct(%d)' % (key)
+    print('getStruct(%d)' % (key))
     return self.log[key]
 
   def zip(self):
-    print 'zip()'
+    print('zip()')
 
 handler = CalculatorHandler()
 processor = Calculator.Processor(handler)
@@ -92,6 +92,6 @@ server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
 #server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
 #server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
 
-print 'Starting the server...'
+print('Starting the server...')
 server.serve()
-print 'done.'
+print('done.')


[4/4] thrift git commit: THRIFT-3409 NodeJS binary field issues Client: Node.js Patch: Nobuaki Sukegawa

Posted by ns...@apache.org.
THRIFT-3409 NodeJS binary field issues
Client: Node.js
Patch: Nobuaki Sukegawa

This closes #681


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

Branch: refs/heads/master
Commit: 8a4d06febe8bc2e1bd84f955b1c2f0149665a0be
Parents: a185d7e
Author: Nobuaki Sukegawa <ns...@apache.org>
Authored: Fri Nov 6 21:24:26 2015 +0900
Committer: Nobuaki Sukegawa <ns...@apache.org>
Committed: Fri Nov 6 21:25:25 2015 +0900

----------------------------------------------------------------------
 lib/nodejs/lib/thrift/binary_protocol.js  | 42 +++++++++++++---------
 lib/nodejs/lib/thrift/compact_protocol.js | 48 +++++++++++++++++---------
 lib/nodejs/lib/thrift/json_protocol.js    | 24 ++++++++++---
 lib/nodejs/test/test_driver.js            | 17 +++++++++
 test/known_failures_Linux.json            | 44 ++++-------------------
 5 files changed, 101 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/8a4d06fe/lib/nodejs/lib/thrift/binary_protocol.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/lib/thrift/binary_protocol.js b/lib/nodejs/lib/thrift/binary_protocol.js
index a230291..25e5634 100644
--- a/lib/nodejs/lib/thrift/binary_protocol.js
+++ b/lib/nodejs/lib/thrift/binary_protocol.js
@@ -146,22 +146,10 @@ TBinaryProtocol.prototype.writeDouble = function(dub) {
   this.trans.write(binary.writeDouble(new Buffer(8), dub));
 };
 
-TBinaryProtocol.prototype.writeString = function(arg) {
+TBinaryProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
   if (typeof(arg) === 'string') {
-    this.writeI32(Buffer.byteLength(arg, 'utf8'));
-    this.trans.write(arg, 'utf8');
-  } else if (arg instanceof Buffer) {
-    this.writeI32(arg.length);
-    this.trans.write(arg);
-  } else {
-    throw new Error('writeString called without a string/Buffer argument: ' + arg);
-  }
-};
-
-TBinaryProtocol.prototype.writeBinary = function(arg) {
-  if (typeof(arg) === 'string') {
-    this.writeI32(Buffer.byteLength(arg, 'utf8'));
-    this.trans.write(arg, 'utf8');
+    this.writeI32(Buffer.byteLength(arg, encoding));
+    this.trans.write(new Buffer(arg, encoding));
   } else if ((arg instanceof Buffer) ||
              (Object.prototype.toString.call(arg) == '[object Uint8Array]')) {
     // Buffers in Node.js under Browserify may extend UInt8Array instead of
@@ -170,10 +158,18 @@ TBinaryProtocol.prototype.writeBinary = function(arg) {
     this.writeI32(arg.length);
     this.trans.write(arg);
   } else {
-    throw new Error('writeBinary called without a string/Buffer argument: ' + arg);
+    throw new Error(name + ' called without a string/Buffer argument: ' + arg);
   }
 };
 
+TBinaryProtocol.prototype.writeString = function(arg) {
+  this.writeStringOrBinary('writeString', 'utf8', arg);
+};
+
+TBinaryProtocol.prototype.writeBinary = function(arg) {
+  this.writeStringOrBinary('writeBinary', 'binary', arg);
+};
+
 TBinaryProtocol.prototype.readMessageBegin = function() {
   var sz = this.readI32();
   var type, name, seqid;
@@ -279,11 +275,25 @@ TBinaryProtocol.prototype.readDouble = function() {
 
 TBinaryProtocol.prototype.readBinary = function() {
   var len = this.readI32();
+  if (len === 0) {
+    return new Buffer();
+  }
+
+  if (len < 0) {
+    throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
+  }
   return this.trans.read(len);
 };
 
 TBinaryProtocol.prototype.readString = function() {
   var len = this.readI32();
+  if (len === 0) {
+    return "";
+  }
+
+  if (len < 0) {
+    throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
+  }
   return this.trans.readString(len);
 };
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/8a4d06fe/lib/nodejs/lib/thrift/compact_protocol.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/lib/thrift/compact_protocol.js b/lib/nodejs/lib/thrift/compact_protocol.js
index 8aee808..deecf48 100644
--- a/lib/nodejs/lib/thrift/compact_protocol.js
+++ b/lib/nodejs/lib/thrift/compact_protocol.js
@@ -429,22 +429,30 @@ TCompactProtocol.prototype.writeDouble = function(v) {
   this.trans.write(buff);
 };
 
-TCompactProtocol.prototype.writeString = function(arg) {
-  this.writeBinary(arg);
-};
-
-TCompactProtocol.prototype.writeBinary = function(arg) {
+TCompactProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
   if (typeof arg === 'string') {
-    this.writeVarint32(Buffer.byteLength(arg, 'utf8')) ;
-    this.trans.write(arg, 'utf8');
-  } else if (arg instanceof Buffer) {
+    this.writeVarint32(Buffer.byteLength(arg, encoding)) ;
+    this.trans.write(new Buffer(arg, encoding));
+  } else if (arg instanceof Buffer ||
+             Object.prototype.toString.call(arg) == '[object Uint8Array]') {
+    // Buffers in Node.js under Browserify may extend UInt8Array instead of
+    // defining a new object. We detect them here so we can write them
+    // correctly
     this.writeVarint32(arg.length);
     this.trans.write(arg);
   } else {
-    throw new Error('writeString/writeBinary called without a string/Buffer argument: ' + arg);
+    throw new Error(name + ' called without a string/Buffer argument: ' + arg);
   }
 };
 
+TCompactProtocol.prototype.writeString = function(arg) {
+  this.writeStringOrBinary('writeString', 'utf8', arg);
+};
+
+TCompactProtocol.prototype.writeBinary = function(arg) {
+  this.writeStringOrBinary('writeBinary', 'binary', arg);
+};
+
 
 //
 // Compact Protocol internal write methods
@@ -752,22 +760,28 @@ TCompactProtocol.prototype.readDouble = function() {
 
 TCompactProtocol.prototype.readBinary = function() {
   var size = this.readVarint32();
-  // Catch empty string case
   if (size === 0) {
-    return "";
+    return new Buffer();
   }
 
-  // Catch error cases
   if (size < 0) {
-    throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary/string size");
+    throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
   }
-  var value = this.trans.readString(size);
-
-  return value;
+  return this.trans.read(size);
 };
 
 TCompactProtocol.prototype.readString = function() {
-  return this.readBinary();
+  var size = this.readVarint32();
+  // Catch empty string case
+  if (size === 0) {
+    return "";
+  }
+
+  // Catch error cases
+  if (size < 0) {
+    throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
+  }
+  return this.trans.readString(size);
 };
 
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/8a4d06fe/lib/nodejs/lib/thrift/json_protocol.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/lib/thrift/json_protocol.js b/lib/nodejs/lib/thrift/json_protocol.js
index 18fb012..436709c 100644
--- a/lib/nodejs/lib/thrift/json_protocol.js
+++ b/lib/nodejs/lib/thrift/json_protocol.js
@@ -317,11 +317,19 @@ TJSONProtocol.prototype.writeDouble = function(dub) {
 };
 
 /** Serializes a string */
-TJSONProtocol.prototype.writeString = function(str) {
+TJSONProtocol.prototype.writeString = function(arg) {
   // We do not encode uri components for wire transfer:
-  if (str === null) {
+  if (arg === null) {
       this.tstack.push(null);
   } else {
+      if (typeof arg === 'string') {
+        var str = arg;
+      } else if (arg instanceof Buffer) {
+        var str = arg.toString('utf8');
+      } else {
+        throw new Error('writeString called without a string/Buffer argument: ' + arg);
+      }
+
       // concat may be slower than building a byte buffer
       var escapedString = '';
       for (var i = 0; i < str.length; i++) {
@@ -358,7 +366,15 @@ TJSONProtocol.prototype.writeString = function(str) {
 
 /** Serializes a string */
 TJSONProtocol.prototype.writeBinary = function(arg) {
-  this.writeString(arg);
+  if (typeof arg === 'string') {
+    var buf = new Buffer(arg, 'binary');
+  } else if (arg instanceof Buffer ||
+             Object.prototype.toString.call(arg) == '[object Uint8Array]')  {
+    var buf = arg;
+  } else {
+    throw new Error('writeBinary called without a string/Buffer argument: ' + arg);
+  }
+  this.tstack.push('"' + buf.toString('base64') + '"');
 };
 
 /**
@@ -680,7 +696,7 @@ TJSONProtocol.prototype.readDouble = function() {
 /** Returns the an object with a value property set to the
     next value found in the protocol buffer */
 TJSONProtocol.prototype.readBinary = function() {
-  return this.readString();
+  return new Buffer(this.readString(), 'base64');
 };
 
 /** Returns the an object with a value property set to the

http://git-wip-us.apache.org/repos/asf/thrift/blob/8a4d06fe/lib/nodejs/test/test_driver.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/test/test_driver.js b/lib/nodejs/test/test_driver.js
index 09439e5..590d583 100644
--- a/lib/nodejs/test/test_driver.js
+++ b/lib/nodejs/test/test_driver.js
@@ -57,6 +57,23 @@ exports.ThriftTestDriver = function(client, callback) {
     testCases.deep.forEach(makeAsserter(assert.deepEqual));
     testCases.deepUnordered.forEach(makeAsserter(makeUnorderedDeepEqual(assert)));
 
+    var arr = [];
+    for (var i = 0; i < 256; ++i) {
+      arr[i] = 255 - i;
+    }
+    var buf = new Buffer(arr);
+    client.testBinary(buf, function(err, response) {
+      assert.error(err, 'testBinary: no callback error');
+      assert.equal(response.length, 256, 'testBinary');
+      assert.deepEqual(response, buf, 'testBinary(Buffer)');
+    });
+    var buf = new Buffer(arr);
+    client.testBinary(buf.toString('binary'), function(err, response) {
+      assert.error(err, 'testBinary: no callback error');
+      assert.equal(response.length, 256, 'testBinary');
+      assert.deepEqual(response, buf, 'testBinary(string)');
+    });
+
     client.testMapMap(42, function(err, response) {
       var expected = {
         "4": {"1":1, "2":2, "3":3, "4":4},

http://git-wip-us.apache.org/repos/asf/thrift/blob/8a4d06fe/test/known_failures_Linux.json
----------------------------------------------------------------------
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index 3625fa9..d326c59 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -1,6 +1,4 @@
 [
-  "c_glib-nodejs_binary_buffered-ip",
-  "c_glib-nodejs_binary_framed-ip",
   "c_glib-py_binary-accel_buffered-ip",
   "c_glib-py_binary-accel_framed-ip",
   "c_glib-py_binary_buffered-ip",
@@ -9,6 +7,7 @@
   "c_glib-rb_binary-accel_framed-ip",
   "c_glib-rb_binary_buffered-ip",
   "c_glib-rb_binary_framed-ip",
+  "cpp-cpp_binary_http-domain",
   "cpp-cpp_binary_http-ip",
   "cpp-cpp_compact_http-domain",
   "cpp-cpp_header_http-domain",
@@ -23,6 +22,7 @@
   "cpp-java_compact_http-ip-ssl",
   "cpp-java_json_http-ip",
   "cpp-java_json_http-ip-ssl",
+  "cpp-nodejs_json_buffered-ip-ssl",
   "cpp-rb_json_buffered-ip",
   "csharp-cpp_binary_buffered-ip-ssl",
   "csharp-cpp_binary_framed-ip-ssl",
@@ -48,7 +48,6 @@
   "csharp-nodejs_compact_framed-ip-ssl",
   "csharp-nodejs_json_buffered-ip",
   "csharp-nodejs_json_buffered-ip-ssl",
-  "csharp-nodejs_json_framed-ip",
   "csharp-nodejs_json_framed-ip-ssl",
   "erl-cpp_compact_buffered-ip",
   "erl-cpp_compact_buffered-ip-ssl",
@@ -97,15 +96,7 @@
   "java-hs_json_buffered-ip",
   "java-hs_json_fastframed-framed-ip",
   "java-hs_json_framed-ip",
-  "java-nodejs_json_buffered-ip",
-  "java-nodejs_json_buffered-ip-ssl",
-  "nodejs-cpp_compact_buffered-ip",
-  "nodejs-cpp_compact_buffered-ip-ssl",
-  "nodejs-cpp_compact_framed-ip",
-  "nodejs-cpp_compact_framed-ip-ssl",
-  "nodejs-csharp_compact_buffered-ip",
   "nodejs-csharp_compact_buffered-ip-ssl",
-  "nodejs-csharp_compact_framed-ip",
   "nodejs-csharp_compact_framed-ip-ssl",
   "nodejs-csharp_json_buffered-ip",
   "nodejs-csharp_json_buffered-ip-ssl",
@@ -113,48 +104,27 @@
   "nodejs-csharp_json_framed-ip-ssl",
   "nodejs-dart_json_buffered-ip",
   "nodejs-dart_json_framed-ip",
+  "nodejs-hs_binary_buffered-ip",
+  "nodejs-hs_binary_framed-ip",
+  "nodejs-hs_compact_buffered-ip",
+  "nodejs-hs_compact_framed-ip",
   "nodejs-hs_json_buffered-ip",
   "nodejs-hs_json_framed-ip",
-  "nodejs-java_compact_buffered-ip",
-  "nodejs-java_compact_buffered-ip-ssl",
-  "nodejs-java_compact_framed-fastframed-ip",
-  "nodejs-java_compact_framed-fastframed-ip-ssl",
-  "nodejs-java_compact_framed-ip",
-  "nodejs-java_compact_framed-ip-ssl",
-  "nodejs-java_json_buffered-ip-ssl",
-  "nodejs-py3_compact_buffered-ip",
-  "nodejs-py3_compact_buffered-ip-ssl",
-  "nodejs-py3_compact_framed-ip",
-  "nodejs-py3_compact_framed-ip-ssl",
   "nodejs-py3_json_buffered-ip",
   "nodejs-py3_json_buffered-ip-ssl",
   "nodejs-py3_json_framed-ip",
   "nodejs-py3_json_framed-ip-ssl",
-  "nodejs-py_compact_buffered-ip",
-  "nodejs-py_compact_buffered-ip-ssl",
-  "nodejs-py_compact_framed-ip",
-  "nodejs-py_compact_framed-ip-ssl",
   "nodejs-py_json_buffered-ip",
   "nodejs-py_json_buffered-ip-ssl",
   "nodejs-py_json_framed-ip",
   "nodejs-py_json_framed-ip-ssl",
-  "nodejs-rb_compact_buffered-ip",
-  "nodejs-rb_compact_framed-ip",
   "nodejs-rb_json_buffered-ip",
   "nodejs-rb_json_framed-ip",
   "perl-php_binary_framed-ip",
   "py-hs_json_buffered-ip",
   "py-hs_json_framed-ip",
-  "py-nodejs_json_buffered-ip",
-  "py-nodejs_json_buffered-ip-ssl",
-  "py-nodejs_json_framed-ip",
-  "py-nodejs_json_framed-ip-ssl",
   "py3-hs_json_buffered-ip",
   "py3-hs_json_framed-ip",
-  "py3-perl_binary_buffered-ip-ssl",
-  "py3-perl_binary_framed-ip-ssl",
   "rb-hs_json_buffered-ip",
-  "rb-hs_json_framed-ip",
-  "rb-nodejs_json_buffered-ip",
-  "rb-nodejs_json_framed-ip"
+  "rb-hs_json_framed-ip"
 ]
\ No newline at end of file


[3/4] thrift git commit: THRIFT-1857 Python 3 Support Client: Python Patch: Nobuaki Sukegawa

Posted by ns...@apache.org.
THRIFT-1857 Python 3 Support
Client: Python
Patch: Nobuaki Sukegawa

Add py3 cross test


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

Branch: refs/heads/master
Commit: a185d7e78589a42e076379ae7165857e5e828e5c
Parents: 760511f
Author: Nobuaki Sukegawa <ns...@apache.org>
Authored: Fri Nov 6 21:24:24 2015 +0900
Committer: Nobuaki Sukegawa <ns...@apache.org>
Committed: Fri Nov 6 21:24:24 2015 +0900

----------------------------------------------------------------------
 Makefile.am                         |   2 +-
 build/docker/centos/Dockerfile      |   2 +-
 build/docker/ubuntu/Dockerfile      |   2 +-
 build/travis/installDependencies.sh |   2 +-
 configure.ac                        |  12 ++
 contrib/Vagrantfile                 |   2 +-
 lib/py/Makefile.am                  |  11 +-
 test/known_failures_Linux.json      |  14 ++
 test/py/TestClient.py               | 129 ++++++++--------
 test/py/TestServer.py               | 251 ++++++++++++++++---------------
 test/tests.json                     |  39 +++++
 11 files changed, 280 insertions(+), 186 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/Makefile.am
----------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
index 73bc48f..812a40a 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,7 @@ empty :=
 space := $(empty) $(empty)
 comma := ,
 
-CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@
+CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@
 CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS))
 
 cross: precross

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/build/docker/centos/Dockerfile
----------------------------------------------------------------------
diff --git a/build/docker/centos/Dockerfile b/build/docker/centos/Dockerfile
index 8d5596a..2ba73c3 100644
--- a/build/docker/centos/Dockerfile
+++ b/build/docker/centos/Dockerfile
@@ -50,7 +50,7 @@ RUN yum install -y libboost-dev libevent-devel
 RUN yum install -y ant junit ant-nodeps ant-junit java-1.7.0-openjdk-devel
 
 # Python Dependencies
-RUN yum install -y python-devel python-setuptools python-twisted
+RUN yum install -y python-devel python-setuptools python-twisted python-six
 
 # Ruby Dependencies
 RUN yum install -y ruby ruby-devel rubygems && \

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/build/docker/ubuntu/Dockerfile
----------------------------------------------------------------------
diff --git a/build/docker/ubuntu/Dockerfile b/build/docker/ubuntu/Dockerfile
index 0b02a70..d8bac0c 100644
--- a/build/docker/ubuntu/Dockerfile
+++ b/build/docker/ubuntu/Dockerfile
@@ -37,7 +37,7 @@ RUN apt-get install -y ant openjdk-7-jdk maven && \
 
 # Python dependencies
 RUN apt-get install -y python-all python-all-dev python-all-dbg python-setuptools python-support \
-    python-twisted python-zope.interface python-six
+    python-twisted python-zope.interface python-six python3-six
 
 # Ruby dependencies
 RUN apt-get install -y ruby ruby-dev && \

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/build/travis/installDependencies.sh
----------------------------------------------------------------------
diff --git a/build/travis/installDependencies.sh b/build/travis/installDependencies.sh
index df12640..4945fd4 100755
--- a/build/travis/installDependencies.sh
+++ b/build/travis/installDependencies.sh
@@ -27,7 +27,7 @@ sudo apt-get install -qq ant openjdk-7-jdk
 sudo update-java-alternatives -s java-1.7.0-openjdk-amd64
 
 # Python dependencies
-sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-twisted python-six
+sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-twisted python-six python3-six
 
 # Ruby dependencies
 sudo apt-get install -qq ruby ruby-dev

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 3d1b15f..777300a 100755
--- a/configure.ac
+++ b/configure.ac
@@ -285,6 +285,16 @@ if test "$with_python" = "yes";  then
 fi
 AM_CONDITIONAL(WITH_PYTHON, [test "$have_python" = "yes"])
 
+# Find "python3" executable.
+# It's distro specific and far from ideal but needed to cross test py2-3 at once.
+if test "x$have_python" = "xyes"; then
+  AC_PATH_PROG([PYTHON3], [python3])
+  if test "x$PYTHON3" != "x" && test "x$PYTHON3" != "x:" ; then
+    have_py3="yes"
+  fi
+fi
+AM_CONDITIONAL(WITH_PY3, [test "$have_py3" = "yes"])
+
 AX_THRIFT_LIB(perl, [Perl], yes)
 if test "$with_perl" = "yes"; then
   AC_PATH_PROG([PERL], [perl])
@@ -782,6 +792,8 @@ if test "$have_csharp" = "yes" ; then MAYBE_CSHARP="csharp" ; else MAYBE_CSHARP=
 AC_SUBST([MAYBE_CSHARP])
 if test "$have_python" = "yes" ; then MAYBE_PYTHON="py" ; else MAYBE_PYTHON="" ; fi
 AC_SUBST([MAYBE_PYTHON])
+if test "$have_py3" = "yes" ; then MAYBE_PY3="py3" ; else MAYBE_PY3="" ; fi
+AC_SUBST([MAYBE_PY3])
 if test "$have_ruby" = "yes" ; then MAYBE_RUBY="rb" ; else MAYBE_RUBY="" ; fi
 AC_SUBST([MAYBE_RUBY])
 if test "$have_haskell" = "yes" ; then MAYBE_HASKELL="hs" ; else MAYBE_HASKELL="" ; fi

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/contrib/Vagrantfile
----------------------------------------------------------------------
diff --git a/contrib/Vagrantfile b/contrib/Vagrantfile
index 2091110..3bcc46a 100644
--- a/contrib/Vagrantfile
+++ b/contrib/Vagrantfile
@@ -46,7 +46,7 @@ sudo apt-get install -qq libboost-dev libboost-test-dev libboost-program-options
 sudo apt-get install -qq ant openjdk-7-jdk maven
 
 # Python dependencies
-sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-six
+sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-six python3-six
 
 # Ruby dependencies
 sudo apt-get install -qq ruby ruby-dev

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/lib/py/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/py/Makefile.am b/lib/py/Makefile.am
index 2cdbb24..b88b6be 100755
--- a/lib/py/Makefile.am
+++ b/lib/py/Makefile.am
@@ -19,7 +19,16 @@
 AUTOMAKE_OPTIONS = serial-tests
 DESTDIR ?= /
 
-all-local:
+if WITH_PY3
+py3-build:
+	$(PYTHON3) setup.py build
+else
+py3-build:
+endif
+
+testing:
+
+all-local: py3-build testing
 	$(PYTHON) setup.py build
 
 # We're ignoring prefix here because site-packages seems to be

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/test/known_failures_Linux.json
----------------------------------------------------------------------
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index 4c78c52..3625fa9 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -88,6 +88,8 @@
   "hs-java_json_framed-ip",
   "hs-nodejs_json_buffered-ip",
   "hs-nodejs_json_framed-ip",
+  "hs-py3_json_buffered-ip",
+  "hs-py3_json_framed-ip",
   "hs-py_json_buffered-ip",
   "hs-py_json_framed-ip",
   "hs-rb_json_buffered-ip",
@@ -120,6 +122,14 @@
   "nodejs-java_compact_framed-ip",
   "nodejs-java_compact_framed-ip-ssl",
   "nodejs-java_json_buffered-ip-ssl",
+  "nodejs-py3_compact_buffered-ip",
+  "nodejs-py3_compact_buffered-ip-ssl",
+  "nodejs-py3_compact_framed-ip",
+  "nodejs-py3_compact_framed-ip-ssl",
+  "nodejs-py3_json_buffered-ip",
+  "nodejs-py3_json_buffered-ip-ssl",
+  "nodejs-py3_json_framed-ip",
+  "nodejs-py3_json_framed-ip-ssl",
   "nodejs-py_compact_buffered-ip",
   "nodejs-py_compact_buffered-ip-ssl",
   "nodejs-py_compact_framed-ip",
@@ -139,6 +149,10 @@
   "py-nodejs_json_buffered-ip-ssl",
   "py-nodejs_json_framed-ip",
   "py-nodejs_json_framed-ip-ssl",
+  "py3-hs_json_buffered-ip",
+  "py3-hs_json_framed-ip",
+  "py3-perl_binary_buffered-ip-ssl",
+  "py3-perl_binary_framed-ip-ssl",
   "rb-hs_json_buffered-ip",
   "rb-hs_json_framed-ip",
   "rb-nodejs_json_buffered-ip",

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/test/py/TestClient.py
----------------------------------------------------------------------
diff --git a/test/py/TestClient.py b/test/py/TestClient.py
index 4689d63..5b858ef 100755
--- a/test/py/TestClient.py
+++ b/test/py/TestClient.py
@@ -26,47 +26,9 @@ import time
 import unittest
 from optparse import OptionParser
 
-parser = OptionParser()
-parser.add_option('--genpydir', type='string', dest='genpydir',
-                  default='gen-py',
-                  help='include this local directory in sys.path for locating generated code')
-parser.add_option("--port", type="int", dest="port",
-    help="connect to server at port")
-parser.add_option("--host", type="string", dest="host",
-    help="connect to server")
-parser.add_option("--zlib", action="store_true", dest="zlib",
-    help="use zlib wrapper for compressed transport")
-parser.add_option("--ssl", action="store_true", dest="ssl",
-    help="use SSL for encrypted transport")
-parser.add_option("--http", dest="http_path",
-    help="Use the HTTP transport with the specified path")
-parser.add_option('-v', '--verbose', action="store_const",
-    dest="verbose", const=2,
-    help="verbose output")
-parser.add_option('-q', '--quiet', action="store_const",
-    dest="verbose", const=0,
-    help="minimal output")
-parser.add_option('--protocol',  dest="proto", type="string",
-    help="protocol to use, one of: accel, binary, compact, json")
-parser.add_option('--transport',  dest="trans", type="string",
-    help="transport to use, one of: buffered, framed")
-parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', port=9090, proto='binary')
-options, args = parser.parse_args()
-
-script_dir = os.path.abspath(os.path.dirname(__file__))
-lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib*')
-sys.path.insert(0, os.path.join(script_dir, options.genpydir))
-sys.path.insert(0, glob.glob(lib_dir)[0])
-
-from ThriftTest import ThriftTest
-from ThriftTest.ttypes import *
-from thrift.transport import TTransport
-from thrift.transport import TSocket
-from thrift.transport import THttpClient
-from thrift.transport import TZlibTransport
-from thrift.protocol import TBinaryProtocol
-from thrift.protocol import TCompactProtocol
-from thrift.protocol import TJSONProtocol
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+ROOT_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR))
+DEFAULT_LIBDIR_GLOB = os.path.join(ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
 
 
 class AbstractTest(unittest.TestCase):
@@ -90,11 +52,10 @@ class AbstractTest(unittest.TestCase):
       if options.zlib:
         self.transport = TZlibTransport.TZlibTransport(self.transport, 9)
     self.transport.open()
-    protocol = self.protocol_factory.getProtocol(self.transport)
+    protocol = self.get_protocol(self.transport)
     self.client = ThriftTest.Client(protocol)
 
   def tearDown(self):
-    # Close!
     self.transport.close()
 
   def testVoid(self):
@@ -190,7 +151,7 @@ class AbstractTest(unittest.TestCase):
 
   def testMap(self):
     print('testMap')
-    x = {0:1, 1:2, 2:3, 3:4, -1:-2}
+    x = {0: 1, 1: 2, 2: 3, 3: 4, -1: -2}
     y = self.client.testMap(x)
     self.assertEqual(y, x)
 
@@ -214,7 +175,7 @@ class AbstractTest(unittest.TestCase):
 
   def testTypedef(self):
     print('testTypedef')
-    x = 0xffffffffffffff # 7 bytes of 0xff
+    x = 0xffffffffffffff  # 7 bytes of 0xff
     y = self.client.testTypedef(x)
     self.assertEqual(y, x)
 
@@ -230,11 +191,11 @@ class AbstractTest(unittest.TestCase):
     print('testMulti')
     xpected = Xtruct(string_thing='Hello2', byte_thing=74, i32_thing=0xff00ff, i64_thing=0xffffffffd0d0)
     y = self.client.testMulti(xpected.byte_thing,
-          xpected.i32_thing,
-          xpected.i64_thing,
-          { 0:'abc' },
-          Numberz.FIVE,
-          0xf0f0f0)
+                              xpected.i32_thing,
+                              xpected.i64_thing,
+                              {0: 'abc'},
+                              Numberz.FIVE,
+                              0xf0f0f0)
     self.assertEqual(y, xpected)
 
   def testException(self):
@@ -248,8 +209,8 @@ class AbstractTest(unittest.TestCase):
       self.assertEqual(x.message, 'Xception')
       # TODO ensure same behavior for repr within generated python variants
       # ensure exception's repr method works
-      #x_repr = repr(x)
-      #self.assertEqual(x_repr, 'Xception(errorCode=1001, message=\'Xception\')')
+      # x_repr = repr(x)
+      # self.assertEqual(x_repr, 'Xception(errorCode=1001, message=\'Xception\')')
 
     try:
       self.client.testException('TException')
@@ -280,31 +241,35 @@ class AbstractTest(unittest.TestCase):
   def testOneway(self):
     print('testOneway')
     start = time.time()
-    self.client.testOneway(1) # type is int, not float
+    self.client.testOneway(1)  # type is int, not float
     end = time.time()
     self.assertTrue(end - start < 3,
                     "oneway sleep took %f sec" % (end - start))
 
   def testOnewayThenNormal(self):
     print('testOnewayThenNormal')
-    self.client.testOneway(1) # type is int, not float
+    self.client.testOneway(1)  # type is int, not float
     self.assertEqual(self.client.testString('Python'), 'Python')
 
 
 class NormalBinaryTest(AbstractTest):
-  protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()
+  def get_protocol(self, transport):
+    return TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport)
 
 
 class CompactTest(AbstractTest):
-  protocol_factory = TCompactProtocol.TCompactProtocolFactory()
+  def get_protocol(self, transport):
+    return TCompactProtocol.TCompactProtocolFactory().getProtocol(transport)
 
 
 class JSONTest(AbstractTest):
-  protocol_factory = TJSONProtocol.TJSONProtocolFactory()
+  def get_protocol(self, transport):
+    return TJSONProtocol.TJSONProtocolFactory().getProtocol(transport)
 
 
 class AcceleratedBinaryTest(AbstractTest):
-  protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory()
+  def get_protocol(self, transport):
+    return TBinaryProtocol.TBinaryProtocolAcceleratedFactory().getProtocol(transport)
 
 
 def suite():
@@ -328,8 +293,54 @@ class OwnArgsTestProgram(unittest.TestProgram):
         if args:
             self.testNames = args
         else:
-            self.testNames = (self.defaultTest,)
+            self.testNames = ([self.defaultTest])
         self.createTests()
 
 if __name__ == "__main__":
+  parser = OptionParser()
+  parser.add_option('--libpydir', type='string', dest='libpydir',
+                    help='include this directory in sys.path for locating library code')
+  parser.add_option('--genpydir', type='string', dest='genpydir',
+                    default='gen-py',
+                    help='include this directory in sys.path for locating generated code')
+  parser.add_option("--port", type="int", dest="port",
+                    help="connect to server at port")
+  parser.add_option("--host", type="string", dest="host",
+                    help="connect to server")
+  parser.add_option("--zlib", action="store_true", dest="zlib",
+                    help="use zlib wrapper for compressed transport")
+  parser.add_option("--ssl", action="store_true", dest="ssl",
+                    help="use SSL for encrypted transport")
+  parser.add_option("--http", dest="http_path",
+                    help="Use the HTTP transport with the specified path")
+  parser.add_option('-v', '--verbose', action="store_const",
+                    dest="verbose", const=2,
+                    help="verbose output")
+  parser.add_option('-q', '--quiet', action="store_const",
+                    dest="verbose", const=0,
+                    help="minimal output")
+  parser.add_option('--protocol', dest="proto", type="string",
+                    help="protocol to use, one of: accel, binary, compact, json")
+  parser.add_option('--transport', dest="trans", type="string",
+                    help="transport to use, one of: buffered, framed")
+  parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', port=9090, proto='binary')
+  options, args = parser.parse_args()
+
+  sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
+  if options.libpydir:
+    sys.path.insert(0, glob.glob(options.libpydir)[0])
+  else:
+    sys.path.insert(0, glob.glob(DEFAULT_LIBDIR_GLOB)[0])
+
+  from ThriftTest import ThriftTest
+  from ThriftTest.ttypes import Xtruct, Xtruct2, Numberz, Xception, Xception2
+  from thrift.Thrift import TException
+  from thrift.transport import TTransport
+  from thrift.transport import TSocket
+  from thrift.transport import THttpClient
+  from thrift.transport import TZlibTransport
+  from thrift.protocol import TBinaryProtocol
+  from thrift.protocol import TCompactProtocol
+  from thrift.protocol import TJSONProtocol
+
   OwnArgsTestProgram(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=1))

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/test/py/TestServer.py
----------------------------------------------------------------------
diff --git a/test/py/TestServer.py b/test/py/TestServer.py
index bc221c0..4fa8894 100755
--- a/test/py/TestServer.py
+++ b/test/py/TestServer.py
@@ -26,55 +26,9 @@ import sys
 import time
 from optparse import OptionParser
 
-# Print TServer log to stdout so that the test-runner can redirect it to log files
-logging.basicConfig(level=logging.DEBUG)
-
-parser = OptionParser()
-parser.add_option('--genpydir', type='string', dest='genpydir',
-                  default='gen-py',
-                  help='include this local directory in sys.path for locating generated code')
-parser.add_option("--port", type="int", dest="port",
-    help="port number for server to listen on")
-parser.add_option("--zlib", action="store_true", dest="zlib",
-    help="use zlib wrapper for compressed transport")
-parser.add_option("--ssl", action="store_true", dest="ssl",
-    help="use SSL for encrypted transport")
-parser.add_option('-v', '--verbose', action="store_const",
-    dest="verbose", const=2,
-    help="verbose output")
-parser.add_option('-q', '--quiet', action="store_const",
-    dest="verbose", const=0,
-    help="minimal output")
-parser.add_option('--protocol',  dest="proto", type="string",
-    help="protocol to use, one of: accel, binary, compact, json")
-parser.add_option('--transport',  dest="trans", type="string",
-    help="transport to use, one of: buffered, framed")
-parser.set_defaults(port=9090, verbose=1, proto='binary')
-options, args = parser.parse_args()
-
-script_dir = os.path.realpath(os.path.dirname(__file__))  # <-- absolute dir the script is in
-lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib*')
-
-sys.path.insert(0, os.path.join(script_dir, options.genpydir))
-sys.path.insert(0, glob.glob(lib_dir)[0])
-
-from ThriftTest import ThriftTest
-from ThriftTest.ttypes import *
-from thrift.Thrift import TException
-from thrift.transport import TTransport
-from thrift.transport import TSocket
-from thrift.transport import TZlibTransport
-from thrift.protocol import TBinaryProtocol
-from thrift.protocol import TCompactProtocol
-from thrift.protocol import TJSONProtocol
-from thrift.server import TServer, TNonblockingServer, THttpServer
-
-PROT_FACTORIES = {
-    'binary': TBinaryProtocol.TBinaryProtocolFactory,
-    'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory,
-    'compact': TCompactProtocol.TCompactProtocolFactory,
-    'json': TJSONProtocol.TJSONProtocolFactory,
-}
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+ROOT_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR))
+DEFAULT_LIBDIR_GLOB = os.path.join(ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
 
 
 class TestHandler(object):
@@ -224,78 +178,133 @@ class TestHandler(object):
                   byte_thing=arg0, i32_thing=arg1, i64_thing=arg2)
 
 
-# set up the protocol factory form the --protocol option
-pfactory_cls = PROT_FACTORIES.get(options.proto, None)
-if pfactory_cls is None:
-  raise AssertionError('Unknown --protocol option: %s' % options.proto)
-pfactory = pfactory_cls()
-
-# get the server type (TSimpleServer, TNonblockingServer, etc...)
-if len(args) > 1:
-  raise AssertionError('Only one server type may be specified, not multiple types.')
-server_type = args[0]
-
-# Set up the handler and processor objects
-handler   = TestHandler()
-processor = ThriftTest.Processor(handler)
-
-# Handle THttpServer as a special case
-if server_type == 'THttpServer':
-  server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
-  server.serve()
-  sys.exit(0)
-
-# set up server transport and transport factory
+def main(options):
+  # Print TServer log to stdout so that the test-runner can redirect it to log files
+  logging.basicConfig(level=logging.DEBUG)
 
-abs_key_path = os.path.join(os.path.dirname(script_dir), 'keys', 'server.pem')
-
-host = None
-if options.ssl:
-  from thrift.transport import TSSLSocket
-  transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path)
-else:
-  transport = TSocket.TServerSocket(host, options.port)
-tfactory = TTransport.TBufferedTransportFactory()
-if options.trans == 'buffered':
-  tfactory = TTransport.TBufferedTransportFactory()
-elif options.trans == 'framed':
-  tfactory = TTransport.TFramedTransportFactory()
-elif options.trans == '':
-  raise AssertionError('Unknown --transport option: %s' % options.trans)
-else:
+  # set up the protocol factory form the --protocol option
+  prot_factories = {
+    'binary': TBinaryProtocol.TBinaryProtocolFactory,
+    'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory,
+    'compact': TCompactProtocol.TCompactProtocolFactory,
+    'json': TJSONProtocol.TJSONProtocolFactory,
+  }
+  pfactory_cls = prot_factories.get(options.proto, None)
+  if pfactory_cls is None:
+    raise AssertionError('Unknown --protocol option: %s' % options.proto)
+  pfactory = pfactory_cls()
+
+  # get the server type (TSimpleServer, TNonblockingServer, etc...)
+  if len(args) > 1:
+    raise AssertionError('Only one server type may be specified, not multiple types.')
+  server_type = args[0]
+
+  # Set up the handler and processor objects
+  handler = TestHandler()
+  processor = ThriftTest.Processor(handler)
+
+  # Handle THttpServer as a special case
+  if server_type == 'THttpServer':
+    server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
+    server.serve()
+    sys.exit(0)
+
+  # set up server transport and transport factory
+
+  abs_key_path = os.path.join(os.path.dirname(SCRIPT_DIR), 'keys', 'server.pem')
+
+  host = None
+  if options.ssl:
+    from thrift.transport import TSSLSocket
+    transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path)
+  else:
+    transport = TSocket.TServerSocket(host, options.port)
   tfactory = TTransport.TBufferedTransportFactory()
-# if --zlib, then wrap server transport, and use a different transport factory
-if options.zlib:
-  transport = TZlibTransport.TZlibTransport(transport)  # wrap  with zlib
-  tfactory = TZlibTransport.TZlibTransportFactory()
-
-# do server-specific setup here:
-if server_type == "TNonblockingServer":
-  server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
-elif server_type == "TProcessPoolServer":
-  import signal
-  from thrift.server import TProcessPoolServer
-  server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
-  server.setNumWorkers(5)
-
-  def set_alarm():
-    def clean_shutdown(signum, frame):
-      for worker in server.workers:
+  if options.trans == 'buffered':
+    tfactory = TTransport.TBufferedTransportFactory()
+  elif options.trans == 'framed':
+    tfactory = TTransport.TFramedTransportFactory()
+  elif options.trans == '':
+    raise AssertionError('Unknown --transport option: %s' % options.trans)
+  else:
+    tfactory = TTransport.TBufferedTransportFactory()
+  # if --zlib, then wrap server transport, and use a different transport factory
+  if options.zlib:
+    transport = TZlibTransport.TZlibTransport(transport)  # wrap  with zlib
+    tfactory = TZlibTransport.TZlibTransportFactory()
+
+  # do server-specific setup here:
+  if server_type == "TNonblockingServer":
+    server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
+  elif server_type == "TProcessPoolServer":
+    import signal
+    from thrift.server import TProcessPoolServer
+    server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
+    server.setNumWorkers(5)
+
+    def set_alarm():
+      def clean_shutdown(signum, frame):
+        for worker in server.workers:
+          if options.verbose > 0:
+            logging.info('Terminating worker: %s' % worker)
+          worker.terminate()
         if options.verbose > 0:
-          logging.info('Terminating worker: %s' % worker)
-        worker.terminate()
-      if options.verbose > 0:
-        logging.info('Requesting server to stop()')
-      try:
-        server.stop()
-      except:
-        pass
-    signal.signal(signal.SIGALRM, clean_shutdown)
-    signal.alarm(4)
-  set_alarm()
-else:
-  # look up server class dynamically to instantiate server
-  ServerClass = getattr(TServer, server_type)
-  server = ServerClass(processor, transport, tfactory, pfactory)
-# enter server main loop
-server.serve()
+          logging.info('Requesting server to stop()')
+        try:
+          server.stop()
+        except:
+          pass
+      signal.signal(signal.SIGALRM, clean_shutdown)
+      signal.alarm(4)
+    set_alarm()
+  else:
+    # look up server class dynamically to instantiate server
+    ServerClass = getattr(TServer, server_type)
+    server = ServerClass(processor, transport, tfactory, pfactory)
+  # enter server main loop
+  server.serve()
+
+if __name__ == '__main__':
+  parser = OptionParser()
+  parser.add_option('--libpydir', type='string', dest='libpydir',
+                    help='include this directory to sys.path for locating library code')
+  parser.add_option('--genpydir', type='string', dest='genpydir',
+                    default='gen-py',
+                    help='include this directory to sys.path for locating generated code')
+  parser.add_option("--port", type="int", dest="port",
+                    help="port number for server to listen on")
+  parser.add_option("--zlib", action="store_true", dest="zlib",
+                    help="use zlib wrapper for compressed transport")
+  parser.add_option("--ssl", action="store_true", dest="ssl",
+                    help="use SSL for encrypted transport")
+  parser.add_option('-v', '--verbose', action="store_const",
+                    dest="verbose", const=2,
+                    help="verbose output")
+  parser.add_option('-q', '--quiet', action="store_const",
+                    dest="verbose", const=0,
+                    help="minimal output")
+  parser.add_option('--protocol', dest="proto", type="string",
+                    help="protocol to use, one of: accel, binary, compact, json")
+  parser.add_option('--transport', dest="trans", type="string",
+                    help="transport to use, one of: buffered, framed")
+  parser.set_defaults(port=9090, verbose=1, proto='binary')
+  options, args = parser.parse_args()
+
+  sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
+  if options.libpydir:
+    sys.path.insert(0, glob.glob(options.libpydir)[0])
+  else:
+    sys.path.insert(0, glob.glob(DEFAULT_LIBDIR_GLOB)[0])
+
+  from ThriftTest import ThriftTest
+  from ThriftTest.ttypes import Xtruct, Xception, Xception2, Insanity
+  from thrift.Thrift import TException
+  from thrift.transport import TTransport
+  from thrift.transport import TSocket
+  from thrift.transport import TZlibTransport
+  from thrift.protocol import TBinaryProtocol
+  from thrift.protocol import TCompactProtocol
+  from thrift.protocol import TJSONProtocol
+  from thrift.server import TServer, TNonblockingServer, THttpServer
+
+  sys.exit(main(options))

http://git-wip-us.apache.org/repos/asf/thrift/blob/a185d7e7/test/tests.json
----------------------------------------------------------------------
diff --git a/test/tests.json b/test/tests.json
index 0c35df2..afc7531 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -202,6 +202,45 @@
     "workdir": "py"
   },
   {
+    "comment": "Using 'python3' executable to test py2 and 3 at once",
+    "name": "py3",
+    "server": {
+      "delay": 1,
+      "extra_args": ["TSimpleServer"],
+      "command": [
+        "python3",
+        "TestServer.py",
+        "--verbose",
+        "--libpydir=../../lib/py/build/lib",
+        "--genpydir=gen-py"
+      ]
+    },
+    "client": {
+      "timeout": 10,
+      "command": [
+        "python3",
+        "TestClient.py",
+        "--host=localhost",
+        "--libpydir=../../lib/py/build/lib",
+        "--genpydir=gen-py"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip-ssl",
+      "ip"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json"
+    ],
+    "workdir": "py"
+  },
+  {
     "name": "cpp",
     "server": {
       "delay": 2,


[2/4] thrift git commit: THRIFT-1857 Python 3 Support Client: Python Patch: Thomas Bartelmess, Eevee (Alex Munroe), helgridly, Christian Verkerk, Jeroen Vlek, Nobuaki Sukegawa

Posted by ns...@apache.org.
THRIFT-1857 Python 3 Support
Client: Python
Patch: Thomas Bartelmess, Eevee (Alex Munroe), helgridly, Christian Verkerk, Jeroen Vlek, Nobuaki Sukegawa

This closes #213 and closes #680


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

Branch: refs/heads/master
Commit: 760511f59b349c59982a64e249e6cf24c2b2f8f6
Parents: 49f4dc0
Author: Nobuaki Sukegawa <ns...@apache.org>
Authored: Fri Nov 6 21:24:16 2015 +0900
Committer: Nobuaki Sukegawa <ns...@apache.org>
Committed: Fri Nov 6 21:24:16 2015 +0900

----------------------------------------------------------------------
 build/docker/ubuntu/Dockerfile              |   2 +-
 build/travis/installDependencies.sh         |   2 +-
 compiler/cpp/src/generate/t_py_generator.cc | 138 +++++++++++++--------
 contrib/Vagrantfile                         |   2 +-
 lib/py/setup.py                             |  13 +-
 lib/py/src/TSCons.py                        |   2 +
 lib/py/src/TSerialization.py                |   4 +-
 lib/py/src/TTornado.py                      |   3 +-
 lib/py/src/compat.py                        |  27 ++++
 lib/py/src/protocol/TBase.py                |  24 ++--
 lib/py/src/protocol/TBinaryProtocol.py      |  10 +-
 lib/py/src/protocol/TCompactProtocol.py     |  24 ++--
 lib/py/src/protocol/TJSONProtocol.py        | 149 ++++++++++++++---------
 lib/py/src/protocol/TProtocol.py            |  31 +++--
 lib/py/src/server/THttpServer.py            |   2 +-
 lib/py/src/server/TNonblockingServer.py     |  25 ++--
 lib/py/src/server/TProcessPoolServer.py     |   2 +-
 lib/py/src/server/TServer.py                |   8 +-
 lib/py/src/transport/THttpClient.py         |  38 +++---
 lib/py/src/transport/TSocket.py             |   2 +-
 lib/py/src/transport/TTransport.py          |  46 +++----
 lib/py/src/transport/TTwisted.py            |   9 +-
 lib/py/src/transport/TZlibTransport.py      |  18 +--
 test/py.tornado/test_suite.py               |   8 +-
 test/py.twisted/test_suite.py               |   2 +-
 test/py/RunClientServer.py                  |  45 +++----
 test/py/SerializationTest.py                |  10 +-
 test/py/TSimpleJSONProtocolTest.py          |  14 +--
 test/py/TestClient.py                       |  21 +++-
 test/py/TestEof.py                          |   4 +-
 test/py/TestServer.py                       |   4 +-
 test/py/TestSocket.py                       |   6 +-
 test/py/TestSyntax.py                       |   2 +-
 tutorial/py.tornado/PythonClient.py         |  18 +--
 tutorial/py.tornado/PythonServer.py         |  16 +--
 tutorial/py.twisted/PythonClient.py         |  16 +--
 tutorial/py.twisted/PythonServer.py         |  14 +--
 tutorial/py.twisted/PythonServer.tac        |   2 +-
 tutorial/py/PythonClient.py                 |  20 +--
 tutorial/py/PythonServer.py                 |  16 +--
 40 files changed, 460 insertions(+), 339 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/build/docker/ubuntu/Dockerfile
----------------------------------------------------------------------
diff --git a/build/docker/ubuntu/Dockerfile b/build/docker/ubuntu/Dockerfile
index 4ad94e1..0b02a70 100644
--- a/build/docker/ubuntu/Dockerfile
+++ b/build/docker/ubuntu/Dockerfile
@@ -37,7 +37,7 @@ RUN apt-get install -y ant openjdk-7-jdk maven && \
 
 # Python dependencies
 RUN apt-get install -y python-all python-all-dev python-all-dbg python-setuptools python-support \
-    python-twisted python-zope.interface
+    python-twisted python-zope.interface python-six
 
 # Ruby dependencies
 RUN apt-get install -y ruby ruby-dev && \

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/build/travis/installDependencies.sh
----------------------------------------------------------------------
diff --git a/build/travis/installDependencies.sh b/build/travis/installDependencies.sh
index 453494c..df12640 100755
--- a/build/travis/installDependencies.sh
+++ b/build/travis/installDependencies.sh
@@ -27,7 +27,7 @@ sudo apt-get install -qq ant openjdk-7-jdk
 sudo update-java-alternatives -s java-1.7.0-openjdk-amd64
 
 # Python dependencies
-sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-twisted
+sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-twisted python-six
 
 # Ruby dependencies
 sudo apt-get install -qq ruby ruby-dev

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/compiler/cpp/src/generate/t_py_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/generate/t_py_generator.cc b/compiler/cpp/src/generate/t_py_generator.cc
index fce29cf..74904ac 100644
--- a/compiler/cpp/src/generate/t_py_generator.cc
+++ b/compiler/cpp/src/generate/t_py_generator.cc
@@ -349,11 +349,18 @@ void t_py_generator::init_generator() {
   f_init.close();
 
   // Print header
-  f_types_ << py_autogen_comment() << endl << py_imports() << endl << render_includes() << endl
-           << render_fastbinary_includes() << endl << endl;
-
-  f_consts_ << py_autogen_comment() << endl << py_imports() << endl << "from ttypes import *"
-            << endl << endl;
+  f_types_ <<
+    py_autogen_comment() << endl <<
+    py_imports() << endl <<
+    render_includes() << endl <<
+    render_fastbinary_includes() <<
+    endl << endl;
+
+  f_consts_ <<
+    py_autogen_comment() << endl <<
+    py_imports() << endl <<
+    "from .ttypes import *" << endl <<
+    endl;
 }
 
 /**
@@ -759,10 +766,12 @@ void t_py_generator::generate_py_struct_definition(ofstream& out,
   if (!gen_slots_) {
     // Printing utilities so that on the command line thrift
     // structs look pretty like dictionaries
-    out << indent() << "def __repr__(self):" << endl << indent() << "  L = ['%s=%r' % (key, value)"
-        << endl << indent() << "    for key, value in self.__dict__.iteritems()]" << endl
-        << indent() << "  return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl
-        << endl;
+    out <<
+      indent() << "def __repr__(self):" << endl <<
+      indent() << "  L = ['%s=%r' % (key, value)" << endl <<
+      indent() << "    for key, value in self.__dict__.items()]" << endl <<
+      indent() << "  return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl <<
+      endl;
 
     // Equality and inequality methods that compare by value
     out << indent() << "def __eq__(self, other):" << endl;
@@ -961,7 +970,7 @@ void t_py_generator::generate_service(t_service* tservice) {
   }
 
   f_service_ << "import logging" << endl
-             << "from ttypes import *" << endl
+             << "from .ttypes import *" << endl
              << "from thrift.Thrift import TProcessor" << endl
              << render_fastbinary_includes() << endl;
 
@@ -1141,26 +1150,32 @@ void t_py_generator::generate_service_client(t_service* tservice) {
   }
 
   if (gen_tornado_ && extends.empty()) {
-    f_service_ << indent() << "@gen.engine" << endl << indent()
-               << "def _start_receiving(self):" << endl << indent() << "  while True:" << endl
-               << indent() << "    try:" << endl << indent()
-               << "      frame = yield self._transport.readFrame()" << endl << indent()
-               << "    except TTransport.TTransportException as e:" << endl << indent()
-               << "      for future in self._reqs.itervalues():" << endl << indent()
-               << "        future.set_exception(e)" << endl << indent() << "      self._reqs = {}"
-               << endl << indent() << "      return" << endl << indent()
-               << "    tr = TTransport.TMemoryBuffer(frame)" << endl << indent()
-               << "    iprot = self._iprot_factory.getProtocol(tr)" << endl << indent()
-               << "    (fname, mtype, rseqid) = iprot.readMessageBegin()" << endl << indent()
-               << "    future = self._reqs.pop(rseqid, None)" << endl << indent()
-               << "    if not future:" << endl << indent()
-               << "      # future has already been discarded" << endl << indent()
-               << "      continue" << endl << indent()
-               << "    method = getattr(self, 'recv_' + fname)" << endl << indent()
-               << "    try:" << endl << indent() << "      result = method(iprot, mtype, rseqid)"
-               << endl << indent() << "    except Exception as e:" << endl << indent()
-               << "      future.set_exception(e)" << endl << indent() << "    else:" << endl
-               << indent() << "      future.set_result(result)" << endl << endl;
+    f_service_ <<
+      indent() << "@gen.engine" << endl <<
+      indent() << "def _start_receiving(self):" << endl <<
+      indent() << "  while True:" << endl <<
+      indent() << "    try:" << endl <<
+      indent() << "      frame = yield self._transport.readFrame()" << endl <<
+      indent() << "    except TTransport.TTransportException as e:" << endl <<
+      indent() << "      for future in self._reqs.values():" << endl <<
+      indent() << "        future.set_exception(e)" << endl <<
+      indent() << "      self._reqs = {}" << endl <<
+      indent() << "      return" << endl <<
+      indent() << "    tr = TTransport.TMemoryBuffer(frame)" << endl <<
+      indent() << "    iprot = self._iprot_factory.getProtocol(tr)" << endl <<
+      indent() << "    (fname, mtype, rseqid) = iprot.readMessageBegin()" << endl <<
+      indent() << "    method = getattr(self, 'recv_' + fname)" << endl <<
+      indent() << "    future = self._reqs.pop(rseqid, None)" << endl <<
+      indent() << "    if not future:" << endl <<
+      indent() << "      # future has already been discarded" << endl <<
+      indent() << "      continue" << endl <<
+      indent() << "    try:" << endl <<
+      indent() << "      result = method(iprot, mtype, rseqid)" << endl <<
+      indent() << "    except Exception as e:" << endl <<
+      indent() << "      future.set_exception(e)" << endl <<
+      indent() << "    else:" << endl <<
+      indent() << "      future.set_result(result)" << endl <<
+      endl;
   }
 
   // Generate client method implementations
@@ -1409,21 +1424,33 @@ void t_py_generator::generate_service_remote(t_service* tservice) {
   ofstream f_remote;
   f_remote.open(f_remote_name.c_str());
 
-  f_remote << "#!/usr/bin/env python" << endl << py_autogen_comment() << endl << "import sys"
-           << endl << "import pprint" << endl << "from urlparse import urlparse" << endl
-           << "from thrift.transport import TTransport" << endl
-           << "from thrift.transport import TSocket" << endl
-           << "from thrift.transport import TSSLSocket" << endl
-           << "from thrift.transport import THttpClient" << endl
-           << "from thrift.protocol import TBinaryProtocol" << endl << endl;
-
-  f_remote << "from " << module_ << " import " << service_name_ << endl << "from " << module_
-           << ".ttypes import *" << endl << endl;
-
-  f_remote << "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << endl << "  print('')" << endl
-           << "  print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] "
-              "function [arg1 [arg2...]]')" << endl << "  print('')" << endl
-           << "  print('Functions:')" << endl;
+  f_remote <<
+    "#!/usr/bin/env python" << endl <<
+    py_autogen_comment() << endl <<
+    "import sys" << endl <<
+    "import pprint" << endl <<
+    "if sys.version_info[0] == 3:" << endl <<
+    "  from urllib.parse import urlparse" << endl <<
+    "else:" << endl <<
+    "  from urlparse import urlparse" << endl <<
+    "from thrift.transport import TTransport" << endl <<
+    "from thrift.transport import TSocket" << endl <<
+    "from thrift.transport import TSSLSocket" << endl <<
+    "from thrift.transport import THttpClient" << endl <<
+    "from thrift.protocol import TBinaryProtocol" << endl <<
+    endl;
+
+  f_remote <<
+    "from " << module_ << " import " << service_name_ << endl <<
+    "from " << module_ << ".ttypes import *" << endl <<
+    endl;
+
+  f_remote <<
+    "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << endl <<
+    "  print('')" << endl <<
+    "  print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] function [arg1 [arg2...]]')" << endl <<
+    "  print('')" << endl <<
+    "  print('Functions:')" << endl;
   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
     f_remote << "  print('  " << (*f_iter)->get_returntype()->get_name() << " "
              << (*f_iter)->get_name() << "(";
@@ -1720,8 +1747,8 @@ void t_py_generator::generate_process_function(t_service* tservice, t_function*
       // Kinda absurd
       f_service_ << indent() << "  error.raiseException()" << endl;
       for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
-        f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as "
-                   << (*x_iter)->get_name() << ":" << endl;
+        f_service_ <<
+          indent() << "except " << type_name((*x_iter)->get_type()) << " as " << (*x_iter)->get_name() << ":" << endl;
         if (!tfunction->is_oneway()) {
           indent_up();
           f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
@@ -1854,11 +1881,15 @@ void t_py_generator::generate_process_function(t_service* tservice, t_function*
       for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
         f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as "
                    << (*x_iter)->get_name() << ":" << endl;
-        indent_up();
-        f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl
-                   << indent() << "result." << (*x_iter)->get_name() << " = "
-                   << (*x_iter)->get_name() << endl;
-        indent_down();
+        if (!tfunction->is_oneway()) {
+          indent_up();
+          f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl;
+          f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
+                     << (*x_iter)->get_name() << endl;
+          indent_down();
+        } else {
+          f_service_ << indent() << "pass" << endl;
+        }
       }
 
       f_service_ << indent() << "except Exception as ex:" << endl
@@ -1989,7 +2020,8 @@ void t_py_generator::generate_deserialize_container(ofstream& out, t_type* ttype
 
   // For loop iterates over elements
   string i = tmp("_i");
-  indent(out) << "for " << i << " in xrange(" << size << "):" << endl;
+  indent(out) <<
+    "for " << i << " in range(" << size << "):" << endl;
 
   indent_up();
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/contrib/Vagrantfile
----------------------------------------------------------------------
diff --git a/contrib/Vagrantfile b/contrib/Vagrantfile
index f967f04..2091110 100644
--- a/contrib/Vagrantfile
+++ b/contrib/Vagrantfile
@@ -46,7 +46,7 @@ sudo apt-get install -qq libboost-dev libboost-test-dev libboost-program-options
 sudo apt-get install -qq ant openjdk-7-jdk maven
 
 # Python dependencies
-sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support
+sudo apt-get install -qq python-all python-all-dev python-all-dbg python-setuptools python-support python-six
 
 # Ruby dependencies
 sudo apt-get install -qq ruby ruby-dev

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/setup.py
----------------------------------------------------------------------
diff --git a/lib/py/setup.py b/lib/py/setup.py
index 7e0a964..090544c 100644
--- a/lib/py/setup.py
+++ b/lib/py/setup.py
@@ -19,6 +19,7 @@
 # under the License.
 #
 
+import platform
 import sys
 try:
     from setuptools import setup, Extension
@@ -69,7 +70,7 @@ def run_setup(with_binary):
         )
     else:
         extensions = dict()
-        
+
     setup(name = 'thrift',
         version = '1.0.0-dev',
         description = 'Python bindings for the Apache Thrift RPC system',
@@ -77,6 +78,7 @@ def run_setup(with_binary):
         author_email = 'dev@thrift.apache.org',
         url = 'http://thrift.apache.org',
         license = 'Apache License 2.0',
+        install_requires=['six>=1.7.2'],
         packages = [
             'thrift',
             'thrift.protocol',
@@ -90,15 +92,20 @@ def run_setup(with_binary):
             'Intended Audience :: Developers',
             'Programming Language :: Python',
             'Programming Language :: Python :: 2',
+            'Programming Language :: Python :: 3',
             'Topic :: Software Development :: Libraries',
             'Topic :: System :: Networking'
         ],
-        use_2to3 = True,
         **extensions
     )
 
 try:
-    run_setup(True)
+    with_binary = False
+    # Don't even try to build the C module unless we're on CPython 2.x.
+    # TODO: fix it for CPython 3.x
+    if platform.python_implementation() == 'CPython' and sys.version_info < (3,):
+        with_binary = True
+    run_setup(with_binary)
 except BuildFailed:
     print()
     print('*' * 80)

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/TSCons.py
----------------------------------------------------------------------
diff --git a/lib/py/src/TSCons.py b/lib/py/src/TSCons.py
index da8d283..ed2601a 100644
--- a/lib/py/src/TSCons.py
+++ b/lib/py/src/TSCons.py
@@ -19,6 +19,8 @@
 
 from os import path
 from SCons.Builder import Builder
+from six.moves import map
+from six.moves import zip
 
 
 def scons_env(env, add=''):

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/TSerialization.py
----------------------------------------------------------------------
diff --git a/lib/py/src/TSerialization.py b/lib/py/src/TSerialization.py
index 8a58d89..fbbe768 100644
--- a/lib/py/src/TSerialization.py
+++ b/lib/py/src/TSerialization.py
@@ -17,8 +17,8 @@
 # under the License.
 #
 
-from protocol import TBinaryProtocol
-from transport import TTransport
+from .protocol import TBinaryProtocol
+from .transport import TTransport
 
 
 def serialize(thrift_object,

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/TTornado.py
----------------------------------------------------------------------
diff --git a/lib/py/src/TTornado.py b/lib/py/src/TTornado.py
index 65795ab..e3b4df7 100644
--- a/lib/py/src/TTornado.py
+++ b/lib/py/src/TTornado.py
@@ -20,11 +20,10 @@
 from __future__ import absolute_import
 import socket
 import struct
-
 import logging
 logger = logging.getLogger(__name__)
 
-from thrift.transport.TTransport import TTransportException, TTransportBase, TMemoryBuffer
+from .transport.TTransport import TTransportException, TTransportBase, TMemoryBuffer
 
 from io import BytesIO
 from collections import deque

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/compat.py
----------------------------------------------------------------------
diff --git a/lib/py/src/compat.py b/lib/py/src/compat.py
new file mode 100644
index 0000000..b2f47dc
--- /dev/null
+++ b/lib/py/src/compat.py
@@ -0,0 +1,27 @@
+import sys
+
+if sys.version_info[0] == 2:
+
+  from cStringIO import StringIO as BufferIO
+
+  def binary_to_str(bin_val):
+    return bin_val
+
+  def str_to_binary(str_val):
+    return str_val
+
+else:
+
+  from io import BytesIO as BufferIO
+
+  def binary_to_str(bin_val):
+    try:
+      return bin_val.decode('utf8')
+    except:
+      return bin_val
+
+  def str_to_binary(str_val):
+    try:
+      return bytearray(str_val, 'utf8')
+    except:
+      return str_val

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/protocol/TBase.py
----------------------------------------------------------------------
diff --git a/lib/py/src/protocol/TBase.py b/lib/py/src/protocol/TBase.py
index 6cbd5f3..118a679 100644
--- a/lib/py/src/protocol/TBase.py
+++ b/lib/py/src/protocol/TBase.py
@@ -17,7 +17,6 @@
 # under the License.
 #
 
-from thrift.Thrift import *
 from thrift.protocol import TBinaryProtocol
 from thrift.transport import TTransport
 
@@ -31,8 +30,7 @@ class TBase(object):
   __slots__ = []
 
   def __repr__(self):
-    L = ['%s=%r' % (key, getattr(self, key))
-              for key in self.__slots__]
+    L = ['%s=%r' % (key, getattr(self, key)) for key in self.__slots__]
     return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
 
   def __eq__(self, other):
@@ -50,9 +48,9 @@ class TBase(object):
 
   def read(self, iprot):
     if (iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and
-        isinstance(iprot.trans, TTransport.CReadableTransport) and
-        self.thrift_spec is not None and
-        fastbinary is not None):
+       isinstance(iprot.trans, TTransport.CReadableTransport) and
+       self.thrift_spec is not None and
+       fastbinary is not None):
       fastbinary.decode_binary(self,
                                iprot.trans,
                                (self.__class__, self.thrift_spec))
@@ -61,21 +59,13 @@ class TBase(object):
 
   def write(self, oprot):
     if (oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and
-        self.thrift_spec is not None and
-        fastbinary is not None):
+       self.thrift_spec is not None and
+       fastbinary is not None):
       oprot.trans.write(
         fastbinary.encode_binary(self, (self.__class__, self.thrift_spec)))
       return
     oprot.writeStruct(self, self.thrift_spec)
 
 
-class TExceptionBase(Exception):
-  # old style class so python2.4 can raise exceptions derived from this
-  #  This can't inherit from TBase because of that limitation.
+class TExceptionBase(TBase, Exception):
   __slots__ = []
-
-  __repr__ = TBase.__repr__.im_func
-  __eq__ = TBase.__eq__.im_func
-  __ne__ = TBase.__ne__.im_func
-  read = TBase.read.im_func
-  write = TBase.write.im_func

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/protocol/TBinaryProtocol.py
----------------------------------------------------------------------
diff --git a/lib/py/src/protocol/TBinaryProtocol.py b/lib/py/src/protocol/TBinaryProtocol.py
index 6fdd08c..f92f558 100644
--- a/lib/py/src/protocol/TBinaryProtocol.py
+++ b/lib/py/src/protocol/TBinaryProtocol.py
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-from TProtocol import *
+from .TProtocol import TType, TProtocolBase, TProtocolException
 from struct import pack, unpack
 
 
@@ -118,7 +118,7 @@ class TBinaryProtocol(TProtocolBase):
     buff = pack("!d", dub)
     self.trans.write(buff)
 
-  def writeString(self, str):
+  def writeBinary(self, str):
     self.writeI32(len(str))
     self.trans.write(str)
 
@@ -217,10 +217,10 @@ class TBinaryProtocol(TProtocolBase):
     val, = unpack('!d', buff)
     return val
 
-  def readString(self):
+  def readBinary(self):
     len = self.readI32()
-    str = self.trans.readAll(len)
-    return str
+    s = self.trans.readAll(len)
+    return s
 
 
 class TBinaryProtocolFactory:

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/protocol/TCompactProtocol.py
----------------------------------------------------------------------
diff --git a/lib/py/src/protocol/TCompactProtocol.py b/lib/py/src/protocol/TCompactProtocol.py
index 7054ab0..b8d171e 100644
--- a/lib/py/src/protocol/TCompactProtocol.py
+++ b/lib/py/src/protocol/TCompactProtocol.py
@@ -17,9 +17,11 @@
 # under the License.
 #
 
-from TProtocol import *
+from .TProtocol import TType, TProtocolBase, TProtocolException, checkIntegerLimits
 from struct import pack, unpack
 
+from ..compat import binary_to_str, str_to_binary
+
 __all__ = ['TCompactProtocol', 'TCompactProtocolFactory']
 
 CLEAR = 0
@@ -62,7 +64,7 @@ def writeVarint(trans, n):
     else:
       out.append((n & 0xff) | 0x80)
       n = n >> 7
-  trans.write(''.join(map(chr, out)))
+  trans.write(bytearray(out))
 
 
 def readVarint(trans):
@@ -141,7 +143,7 @@ class TCompactProtocol(TProtocolBase):
     self.__writeUByte(self.PROTOCOL_ID)
     self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT))
     self.__writeVarint(seqid)
-    self.__writeString(name)
+    self.__writeBinary(str_to_binary(name))
     self.state = VALUE_WRITE
 
   def writeMessageEnd(self):
@@ -254,10 +256,10 @@ class TCompactProtocol(TProtocolBase):
   def writeDouble(self, dub):
     self.trans.write(pack('<d', dub))
 
-  def __writeString(self, s):
+  def __writeBinary(self, s):
     self.__writeSize(len(s))
     self.trans.write(s)
-  writeString = writer(__writeString)
+  writeBinary = writer(__writeBinary)
 
   def readFieldBegin(self):
     assert self.state == FIELD_READ, self.state
@@ -302,7 +304,7 @@ class TCompactProtocol(TProtocolBase):
   def __readSize(self):
     result = self.__readVarint()
     if result < 0:
-      raise TException("Length < 0")
+      raise TProtocolException("Length < 0")
     return result
 
   def readMessageBegin(self):
@@ -310,15 +312,15 @@ class TCompactProtocol(TProtocolBase):
     proto_id = self.__readUByte()
     if proto_id != self.PROTOCOL_ID:
       raise TProtocolException(TProtocolException.BAD_VERSION,
-          'Bad protocol id in the message: %d' % proto_id)
+                               'Bad protocol id in the message: %d' % proto_id)
     ver_type = self.__readUByte()
     type = (ver_type >> self.TYPE_SHIFT_AMOUNT) & self.TYPE_BITS
     version = ver_type & self.VERSION_MASK
     if version != self.VERSION:
       raise TProtocolException(TProtocolException.BAD_VERSION,
-          'Bad version: %d (expect %d)' % (version, self.VERSION))
+                               'Bad version: %d (expect %d)' % (version, self.VERSION))
     seqid = self.__readVarint()
-    name = self.__readString()
+    name = binary_to_str(self.__readBinary())
     return (name, type, seqid)
 
   def readMessageEnd(self):
@@ -388,10 +390,10 @@ class TCompactProtocol(TProtocolBase):
     val, = unpack('<d', buff)
     return val
 
-  def __readString(self):
+  def __readBinary(self):
     len = self.__readSize()
     return self.trans.readAll(len)
-  readString = reader(__readString)
+  readBinary = reader(__readBinary)
 
   def __getTType(self, byte):
     return TTYPES[byte & 0x0f]

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/protocol/TJSONProtocol.py
----------------------------------------------------------------------
diff --git a/lib/py/src/protocol/TJSONProtocol.py b/lib/py/src/protocol/TJSONProtocol.py
index 7807a6c..3ed8bcb 100644
--- a/lib/py/src/protocol/TJSONProtocol.py
+++ b/lib/py/src/protocol/TJSONProtocol.py
@@ -17,11 +17,13 @@
 # under the License.
 #
 
-from TProtocol import TType, TProtocolBase, TProtocolException, \
-    checkIntegerLimits
+from .TProtocol import TType, TProtocolBase, TProtocolException, checkIntegerLimits
 import base64
-import json
 import math
+import sys
+
+from ..compat import str_to_binary
+
 
 __all__ = ['TJSONProtocol',
            'TJSONProtocolFactory',
@@ -30,20 +32,39 @@ __all__ = ['TJSONProtocol',
 
 VERSION = 1
 
-COMMA = ','
-COLON = ':'
-LBRACE = '{'
-RBRACE = '}'
-LBRACKET = '['
-RBRACKET = ']'
-QUOTE = '"'
-BACKSLASH = '\\'
-ZERO = '0'
-
-ESCSEQ = '\\u00'
-ESCAPE_CHAR = '"\\bfnrt/'
-ESCAPE_CHAR_VALS = ['"', '\\', '\b', '\f', '\n', '\r', '\t', '/']
-NUMERIC_CHAR = '+-.0123456789Ee'
+COMMA = b','
+COLON = b':'
+LBRACE = b'{'
+RBRACE = b'}'
+LBRACKET = b'['
+RBRACKET = b']'
+QUOTE = b'"'
+BACKSLASH = b'\\'
+ZERO = b'0'
+
+ESCSEQ0 = ord('\\')
+ESCSEQ1 = ord('u')
+ESCAPE_CHAR_VALS = {
+  '"': '\\"',
+  '\\': '\\\\',
+  '\b': '\\b',
+  '\f': '\\f',
+  '\n': '\\n',
+  '\r': '\\r',
+  '\t': '\\t',
+  # '/': '\\/',
+}
+ESCAPE_CHARS = {
+    b'"': '"',
+    b'\\': '\\',
+    b'b': '\b',
+    b'f': '\f',
+    b'n': '\n',
+    b'r': '\r',
+    b't': '\t',
+    b'/': '/',
+}
+NUMERIC_CHAR = b'+-.0123456789Ee'
 
 CTYPES = {TType.BOOL:       'tf',
           TType.BYTE:       'i8',
@@ -70,7 +91,7 @@ class JSONBaseContext(object):
 
   def doIO(self, function):
     pass
-  
+
   def write(self):
     pass
 
@@ -85,7 +106,7 @@ class JSONBaseContext(object):
 
 
 class JSONListContext(JSONBaseContext):
-    
+
   def doIO(self, function):
     if self.first is True:
       self.first = False
@@ -100,7 +121,7 @@ class JSONListContext(JSONBaseContext):
 
 
 class JSONPairContext(JSONBaseContext):
-  
+
   def __init__(self, protocol):
     super(JSONPairContext, self).__init__(protocol)
     self.colon = True
@@ -146,6 +167,7 @@ class LookaheadReader():
     self.hasData = True
     return self.data
 
+
 class TJSONProtocolBase(TProtocolBase):
 
   def __init__(self, trans):
@@ -174,14 +196,22 @@ class TJSONProtocolBase(TProtocolBase):
 
   def writeJSONString(self, string):
     self.context.write()
-    self.trans.write(json.dumps(string, ensure_ascii=False))
+    json_str = ['"']
+    for s in string:
+      escaped = ESCAPE_CHAR_VALS.get(s, s)
+      json_str.append(escaped)
+    json_str.append('"')
+    self.trans.write(str_to_binary(''.join(json_str)))
 
   def writeJSONNumber(self, number, formatter='{}'):
     self.context.write()
-    jsNumber = formatter.format(number)
+    jsNumber = str(formatter.format(number)).encode('ascii')
     if self.context.escapeNum():
-      jsNumber = "%s%s%s" % (QUOTE, jsNumber, QUOTE)
-    self.trans.write(jsNumber)
+      self.trans.write(QUOTE)
+      self.trans.write(jsNumber)
+      self.trans.write(QUOTE)
+    else:
+      self.trans.write(jsNumber)
 
   def writeJSONBase64(self, binary):
     self.context.write()
@@ -222,18 +252,23 @@ class TJSONProtocolBase(TProtocolBase):
       character = self.reader.read()
       if character == QUOTE:
         break
-      if character == ESCSEQ[0]:
+      if ord(character) == ESCSEQ0:
         character = self.reader.read()
-        if character == ESCSEQ[1]:
-          self.readJSONSyntaxChar(ZERO)
-          self.readJSONSyntaxChar(ZERO)
-          character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
+        if ord(character) == ESCSEQ1:
+          character = chr(int(self.trans.read(4)))
         else:
-          off = ESCAPE_CHAR.find(character)
-          if off == -1:
+          if character not in ESCAPE_CHARS:
             raise TProtocolException(TProtocolException.INVALID_DATA,
                                      "Expected control char")
-          character = ESCAPE_CHAR_VALS[off]
+          character = ESCAPE_CHARS[character]
+      elif character in ESCAPE_CHAR_VALS:
+        raise TProtocolException(TProtocolException.INVALID_DATA,
+                                 "Unescaped control char")
+      elif sys.version_info[0] > 2:
+        utf8_bytes = bytearray([ord(character)])
+        while ord(self.reader.peek()) >= 0x80:
+          utf8_bytes.append(ord(self.reader.read()))
+        character = utf8_bytes.decode('utf8')
       string.append(character)
     return ''.join(string)
 
@@ -251,7 +286,7 @@ class TJSONProtocolBase(TProtocolBase):
       if self.isJSONNumeric(character) is False:
         break
       numeric.append(self.reader.read())
-    return ''.join(numeric)
+    return b''.join(numeric).decode('ascii')
 
   def readJSONInteger(self):
     self.context.read()
@@ -267,12 +302,12 @@ class TJSONProtocolBase(TProtocolBase):
   def readJSONDouble(self):
     self.context.read()
     if self.reader.peek() == QUOTE:
-      string  = self.readJSONString(True)
+      string = self.readJSONString(True)
       try:
         double = float(string)
         if (self.context.escapeNum is False and
-            not math.isinf(double) and
-            not math.isnan(double)):
+           not math.isinf(double) and
+           not math.isnan(double)):
           raise TProtocolException(TProtocolException.INVALID_DATA,
                                    "Numeric data unexpectedly quoted")
         return double
@@ -430,12 +465,12 @@ class TJSONProtocol(TJSONProtocolBase):
   def writeMapEnd(self):
     self.writeJSONObjectEnd()
     self.writeJSONArrayEnd()
-    
+
   def writeListBegin(self, etype, size):
     self.writeJSONArrayStart()
     self.writeJSONString(CTYPES[etype])
     self.writeJSONNumber(size)
-    
+
   def writeListEnd(self):
     self.writeJSONArrayEnd()
 
@@ -443,7 +478,7 @@ class TJSONProtocol(TJSONProtocolBase):
     self.writeJSONArrayStart()
     self.writeJSONString(CTYPES[etype])
     self.writeJSONNumber(size)
-    
+
   def writeSetEnd(self):
     self.writeJSONArrayEnd()
 
@@ -472,7 +507,7 @@ class TJSONProtocol(TJSONProtocolBase):
 
   def writeString(self, string):
     self.writeJSONString(string)
-    
+
   def writeBinary(self, binary):
     self.writeJSONBase64(binary)
 
@@ -485,49 +520,49 @@ class TJSONProtocolFactory:
 
 class TSimpleJSONProtocol(TJSONProtocolBase):
     """Simple, readable, write-only JSON protocol.
-    
+
     Useful for interacting with scripting languages.
     """
 
     def readMessageBegin(self):
         raise NotImplementedError()
-    
+
     def readMessageEnd(self):
         raise NotImplementedError()
-    
+
     def readStructBegin(self):
         raise NotImplementedError()
-    
+
     def readStructEnd(self):
         raise NotImplementedError()
-    
+
     def writeMessageBegin(self, name, request_type, seqid):
         self.resetWriteContext()
-    
+
     def writeMessageEnd(self):
         pass
-    
+
     def writeStructBegin(self, name):
         self.writeJSONObjectStart()
-    
+
     def writeStructEnd(self):
         self.writeJSONObjectEnd()
-      
+
     def writeFieldBegin(self, name, ttype, fid):
         self.writeJSONString(name)
-    
+
     def writeFieldEnd(self):
         pass
-    
+
     def writeMapBegin(self, ktype, vtype, size):
         self.writeJSONObjectStart()
-    
+
     def writeMapEnd(self):
         self.writeJSONObjectEnd()
-    
+
     def _writeCollectionBegin(self, etype, size):
         self.writeJSONArrayStart()
-    
+
     def _writeCollectionEnd(self):
         self.writeJSONArrayEnd()
     writeListBegin = _writeCollectionBegin
@@ -550,16 +585,16 @@ class TSimpleJSONProtocol(TJSONProtocolBase):
     def writeI64(self, i64):
         checkIntegerLimits(i64, 64)
         self.writeJSONNumber(i64)
-    
+
     def writeBool(self, boolean):
         self.writeJSONNumber(1 if boolean is True else 0)
 
     def writeDouble(self, dbl):
         self.writeJSONNumber(dbl)
-    
+
     def writeString(self, string):
         self.writeJSONString(string)
-      
+
     def writeBinary(self, binary):
         self.writeJSONBase64(binary)
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/protocol/TProtocol.py
----------------------------------------------------------------------
diff --git a/lib/py/src/protocol/TProtocol.py b/lib/py/src/protocol/TProtocol.py
index 311a635..22339c0 100644
--- a/lib/py/src/protocol/TProtocol.py
+++ b/lib/py/src/protocol/TProtocol.py
@@ -17,7 +17,10 @@
 # under the License.
 #
 
-from thrift.Thrift import *
+from thrift.Thrift import TException, TType
+import six
+
+from ..compat import binary_to_str, str_to_binary
 
 
 class TProtocolException(TException):
@@ -100,6 +103,9 @@ class TProtocolBase:
     pass
 
   def writeString(self, str_val):
+    self.writeBinary(str_to_binary(str_val))
+
+  def writeBinary(self, str_val):
     pass
 
   def readMessageBegin(self):
@@ -157,6 +163,9 @@ class TProtocolBase:
     pass
 
   def readString(self):
+    return binary_to_str(self.readBinary())
+
+  def readBinary(self):
     pass
 
   def skip(self, ttype):
@@ -187,18 +196,18 @@ class TProtocolBase:
       self.readStructEnd()
     elif ttype == TType.MAP:
       (ktype, vtype, size) = self.readMapBegin()
-      for i in xrange(size):
+      for i in range(size):
         self.skip(ktype)
         self.skip(vtype)
       self.readMapEnd()
     elif ttype == TType.SET:
       (etype, size) = self.readSetBegin()
-      for i in xrange(size):
+      for i in range(size):
         self.skip(etype)
       self.readSetEnd()
     elif ttype == TType.LIST:
       (etype, size) = self.readListBegin()
-      for i in xrange(size):
+      for i in range(size):
         self.skip(etype)
       self.readListEnd()
 
@@ -246,13 +255,13 @@ class TProtocolBase:
     (list_type, list_len) = self.readListBegin()
     if tspec is None:
       # list values are simple types
-      for idx in xrange(list_len):
+      for idx in range(list_len):
         results.append(reader())
     else:
       # this is like an inlined readFieldByTType
       container_reader = self._TTYPE_HANDLERS[list_type][0]
       val_reader = getattr(self, container_reader)
-      for idx in xrange(list_len):
+      for idx in range(list_len):
         val = val_reader(tspec)
         results.append(val)
     self.readListEnd()
@@ -266,12 +275,12 @@ class TProtocolBase:
     (set_type, set_len) = self.readSetBegin()
     if tspec is None:
       # set members are simple types
-      for idx in xrange(set_len):
+      for idx in range(set_len):
         results.add(reader())
     else:
       container_reader = self._TTYPE_HANDLERS[set_type][0]
       val_reader = getattr(self, container_reader)
-      for idx in xrange(set_len):
+      for idx in range(set_len):
         results.add(val_reader(tspec))
     self.readSetEnd()
     return results
@@ -292,7 +301,7 @@ class TProtocolBase:
     key_reader = getattr(self, self._TTYPE_HANDLERS[key_ttype][0])
     val_reader = getattr(self, self._TTYPE_HANDLERS[val_ttype][0])
     # list values are simple types
-    for idx in xrange(map_len):
+    for idx in range(map_len):
       if key_spec is None:
         k_val = key_reader()
       else:
@@ -363,7 +372,7 @@ class TProtocolBase:
     k_writer = getattr(self, ktype_name)
     v_writer = getattr(self, vtype_name)
     self.writeMapBegin(k_type, v_type, len(val))
-    for m_key, m_val in val.iteritems():
+    for m_key, m_val in six.iteritems(val):
       if not k_is_container:
         k_writer(m_key)
       else:
@@ -402,6 +411,7 @@ class TProtocolBase:
     else:
       writer(val)
 
+
 def checkIntegerLimits(i, bits):
     if bits == 8 and (i < -128 or i > 127):
         raise TProtocolException(TProtocolException.INVALID_DATA,
@@ -416,6 +426,7 @@ def checkIntegerLimits(i, bits):
          raise TProtocolException(TProtocolException.INVALID_DATA,
                                   "i64 requires -9223372036854775808 <= number <= 9223372036854775807")
 
+
 class TProtocolFactory:
   def getProtocol(self, trans):
     pass

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/server/THttpServer.py
----------------------------------------------------------------------
diff --git a/lib/py/src/server/THttpServer.py b/lib/py/src/server/THttpServer.py
index 6f92173..bf3b0e3 100644
--- a/lib/py/src/server/THttpServer.py
+++ b/lib/py/src/server/THttpServer.py
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-import BaseHTTPServer
+from six.moves import BaseHTTPServer
 
 from thrift.server import TServer
 from thrift.transport import TTransport

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/server/TNonblockingServer.py
----------------------------------------------------------------------
diff --git a/lib/py/src/server/TNonblockingServer.py b/lib/py/src/server/TNonblockingServer.py
index 39486cd..79748b7 100644
--- a/lib/py/src/server/TNonblockingServer.py
+++ b/lib/py/src/server/TNonblockingServer.py
@@ -26,13 +26,14 @@ maximum connections
 """
 import threading
 import socket
-import Queue
 import select
 import struct
 
 import logging
 logger = logging.getLogger(__name__)
 
+from six.moves import queue
+
 from thrift.transport import TTransport
 from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory
 
@@ -57,7 +58,7 @@ class Worker(threading.Thread):
                 callback(True, otrans.getvalue())
             except Exception:
                 logger.exception("Exception while processing request")
-                callback(False, '')
+                callback(False, b'')
 
 WAIT_LEN = 0
 WAIT_MESSAGE = 1
@@ -104,7 +105,7 @@ class Connection:
         self.socket.setblocking(False)
         self.status = WAIT_LEN
         self.len = 0
-        self.message = ''
+        self.message = b''
         self.lock = threading.Lock()
         self.wake_up = wake_up
 
@@ -132,7 +133,7 @@ class Connection:
                 logger.error("empty frame, it's really strange")
                 self.close()
             else:
-                self.message = ''
+                self.message = b''
                 self.status = WAIT_MESSAGE
 
     @socket_exception
@@ -162,7 +163,7 @@ class Connection:
         sent = self.socket.send(self.message)
         if sent == len(self.message):
             self.status = WAIT_LEN
-            self.message = ''
+            self.message = b''
             self.len = 0
         else:
             self.message = self.message[sent:]
@@ -185,10 +186,10 @@ class Connection:
             self.close()
             self.wake_up()
             return
-        self.len = ''
+        self.len = 0
         if len(message) == 0:
             # it was a oneway request, do not write answer
-            self.message = ''
+            self.message = b''
             self.status = WAIT_LEN
         else:
             self.message = struct.pack('!i', len(message)) + message
@@ -236,7 +237,7 @@ class TNonblockingServer:
         self.out_protocol = outputProtocolFactory or self.in_protocol
         self.threads = int(threads)
         self.clients = {}
-        self.tasks = Queue.Queue()
+        self.tasks = queue.Queue()
         self._read, self._write = socket.socketpair()
         self.prepared = False
         self._stop = False
@@ -252,7 +253,7 @@ class TNonblockingServer:
         if self.prepared:
             return
         self.socket.listen()
-        for _ in xrange(self.threads):
+        for _ in range(self.threads):
             thread = Worker(self.tasks)
             thread.setDaemon(True)
             thread.start()
@@ -269,7 +270,7 @@ class TNonblockingServer:
         In this case, we can just write anything to the second socket from
         socketpair.
         """
-        self._write.send('1')
+        self._write.send(b'1')
 
     def stop(self):
         """Stop the server.
@@ -290,7 +291,7 @@ class TNonblockingServer:
         """Does select on open connections."""
         readable = [self.socket.handle.fileno(), self._read.fileno()]
         writable = []
-        for i, connection in self.clients.items():
+        for i, connection in list(self.clients.items()):
             if connection.is_readable():
                 readable.append(connection.fileno())
             if connection.is_writeable():
@@ -332,7 +333,7 @@ class TNonblockingServer:
 
     def close(self):
         """Closes the server."""
-        for _ in xrange(self.threads):
+        for _ in range(self.threads):
             self.tasks.put([None, None, None, None, None])
         self.socket.close()
         self.prepared = False

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/server/TProcessPoolServer.py
----------------------------------------------------------------------
diff --git a/lib/py/src/server/TProcessPoolServer.py b/lib/py/src/server/TProcessPoolServer.py
index ae7fe1c..b2c2308 100644
--- a/lib/py/src/server/TProcessPoolServer.py
+++ b/lib/py/src/server/TProcessPoolServer.py
@@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
 
 from multiprocessing import  Process, Value, Condition, reduction
 
-from TServer import TServer
+from .TServer import TServer
 from thrift.transport.TTransport import TTransportException
 
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/server/TServer.py
----------------------------------------------------------------------
diff --git a/lib/py/src/server/TServer.py b/lib/py/src/server/TServer.py
index 8c58e39..130f730 100644
--- a/lib/py/src/server/TServer.py
+++ b/lib/py/src/server/TServer.py
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-import Queue
+from six.moves import queue
 import os
 import sys
 import threading
@@ -139,7 +139,7 @@ class TThreadPoolServer(TServer):
 
   def __init__(self, *args, **kwargs):
     TServer.__init__(self, *args)
-    self.clients = Queue.Queue()
+    self.clients = queue.Queue()
     self.threads = 10
     self.daemon = kwargs.get("daemon", False)
 
@@ -250,7 +250,7 @@ class TForkingServer(TServer):
             try:
               while True:
                 self.processor.process(iprot, oprot)
-            except TTransport.TTransportException as tx:
+            except TTransport.TTransportException:
               pass
             except Exception as e:
               logger.exception(e)
@@ -261,7 +261,7 @@ class TForkingServer(TServer):
 
           os._exit(ecode)
 
-      except TTransport.TTransportException as tx:
+      except TTransport.TTransportException:
         pass
       except Exception as x:
         logger.exception(x)

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/transport/THttpClient.py
----------------------------------------------------------------------
diff --git a/lib/py/src/transport/THttpClient.py b/lib/py/src/transport/THttpClient.py
index 5851fa2..5abd41c 100644
--- a/lib/py/src/transport/THttpClient.py
+++ b/lib/py/src/transport/THttpClient.py
@@ -17,17 +17,17 @@
 # under the License.
 #
 
-import httplib
+from io import BytesIO
 import os
 import socket
 import sys
-import urllib
-import urlparse
 import warnings
 
-from cStringIO import StringIO
+from six.moves import urllib
+from six.moves import http_client
 
-from TTransport import *
+from .TTransport import *
+import six
 
 
 class THttpClient(TTransportBase):
@@ -52,31 +52,33 @@ class THttpClient(TTransportBase):
       self.path = path
       self.scheme = 'http'
     else:
-      parsed = urlparse.urlparse(uri_or_host)
+      parsed = urllib.parse.urlparse(uri_or_host)
       self.scheme = parsed.scheme
       assert self.scheme in ('http', 'https')
       if self.scheme == 'http':
-        self.port = parsed.port or httplib.HTTP_PORT
+        self.port = parsed.port or http_client.HTTP_PORT
       elif self.scheme == 'https':
-        self.port = parsed.port or httplib.HTTPS_PORT
+        self.port = parsed.port or http_client.HTTPS_PORT
       self.host = parsed.hostname
       self.path = parsed.path
       if parsed.query:
         self.path += '?%s' % parsed.query
-    self.__wbuf = StringIO()
+    self.__wbuf = BytesIO()
     self.__http = None
+    self.__http_response = None
     self.__timeout = None
     self.__custom_headers = None
 
   def open(self):
     if self.scheme == 'http':
-      self.__http = httplib.HTTP(self.host, self.port)
+      self.__http = http_client.HTTPConnection(self.host, self.port)
     else:
-      self.__http = httplib.HTTPS(self.host, self.port)
+      self.__http = http_client.HTTPSConnection(self.host, self.port)
 
   def close(self):
     self.__http.close()
     self.__http = None
+    self.__http_response = None
 
   def isOpen(self):
     return self.__http is not None
@@ -94,7 +96,7 @@ class THttpClient(TTransportBase):
     self.__custom_headers = headers
 
   def read(self, sz):
-    return self.__http.file.read(sz)
+    return self.__http_response.read(sz)
 
   def write(self, buf):
     self.__wbuf.write(buf)
@@ -117,13 +119,12 @@ class THttpClient(TTransportBase):
 
     # Pull data out of buffer
     data = self.__wbuf.getvalue()
-    self.__wbuf = StringIO()
+    self.__wbuf = BytesIO()
 
     # HTTP request
     self.__http.putrequest('POST', self.path)
 
     # Write headers
-    self.__http.putheader('Host', self.host)
     self.__http.putheader('Content-Type', 'application/x-thrift')
     self.__http.putheader('Content-Length', str(len(data)))
 
@@ -131,11 +132,11 @@ class THttpClient(TTransportBase):
       user_agent = 'Python/THttpClient'
       script = os.path.basename(sys.argv[0])
       if script:
-        user_agent = '%s (%s)' % (user_agent, urllib.quote(script))
+        user_agent = '%s (%s)' % (user_agent, urllib.parse.quote(script))
       self.__http.putheader('User-Agent', user_agent)
 
     if self.__custom_headers:
-        for key, val in self.__custom_headers.iteritems():
+        for key, val in six.iteritems(self.__custom_headers):
             self.__http.putheader(key, val)
 
     self.__http.endheaders()
@@ -144,7 +145,10 @@ class THttpClient(TTransportBase):
     self.__http.send(data)
 
     # Get reply to flush the request
-    self.code, self.message, self.headers = self.__http.getreply()
+    self.__http_response = self.__http.getresponse()
+    self.code = self.__http_response.status
+    self.message = self.__http_response.reason
+    self.headers = self.__http_response.msg
 
   # Decorate if we know how to timeout
   if hasattr(socket, 'getdefaulttimeout'):

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/transport/TSocket.py
----------------------------------------------------------------------
diff --git a/lib/py/src/transport/TSocket.py b/lib/py/src/transport/TSocket.py
index 7b564aa..cb204a4 100644
--- a/lib/py/src/transport/TSocket.py
+++ b/lib/py/src/transport/TSocket.py
@@ -22,7 +22,7 @@ import os
 import socket
 import sys
 
-from TTransport import *
+from .TTransport import *
 
 
 class TSocketBase(TTransportBase):

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/transport/TTransport.py
----------------------------------------------------------------------
diff --git a/lib/py/src/transport/TTransport.py b/lib/py/src/transport/TTransport.py
index 5914aca..3fe289a 100644
--- a/lib/py/src/transport/TTransport.py
+++ b/lib/py/src/transport/TTransport.py
@@ -17,9 +17,9 @@
 # under the License.
 #
 
-from cStringIO import StringIO
 from struct import pack, unpack
 from thrift.Thrift import TException
+from ..compat import BufferIO
 
 
 class TTransportException(TException):
@@ -52,7 +52,7 @@ class TTransportBase:
     pass
 
   def readAll(self, sz):
-    buff = ''
+    buff = b''
     have = 0
     while (have < sz):
       chunk = self.read(sz - have)
@@ -138,8 +138,8 @@ class TBufferedTransport(TTransportBase, CReadableTransport):
 
   def __init__(self, trans, rbuf_size=DEFAULT_BUFFER):
     self.__trans = trans
-    self.__wbuf = StringIO()
-    self.__rbuf = StringIO("")
+    self.__wbuf = BufferIO()
+    self.__rbuf = BufferIO()
     self.__rbuf_size = rbuf_size
 
   def isOpen(self):
@@ -155,8 +155,7 @@ class TBufferedTransport(TTransportBase, CReadableTransport):
     ret = self.__rbuf.read(sz)
     if len(ret) != 0:
       return ret
-
-    self.__rbuf = StringIO(self.__trans.read(max(sz, self.__rbuf_size)))
+    self.__rbuf = BufferIO(self.__trans.read(max(sz, self.__rbuf_size)))
     return self.__rbuf.read(sz)
 
   def write(self, buf):
@@ -164,13 +163,14 @@ class TBufferedTransport(TTransportBase, CReadableTransport):
       self.__wbuf.write(buf)
     except Exception as e:
       # on exception reset wbuf so it doesn't contain a partial function call
-      self.__wbuf = StringIO()
+      self.__wbuf = BufferIO()
       raise e
+    self.__wbuf.getvalue()
 
   def flush(self):
     out = self.__wbuf.getvalue()
     # reset wbuf before write/flush to preserve state on underlying failure
-    self.__wbuf = StringIO()
+    self.__wbuf = BufferIO()
     self.__trans.write(out)
     self.__trans.flush()
 
@@ -189,12 +189,12 @@ class TBufferedTransport(TTransportBase, CReadableTransport):
     if len(retstring) < reqlen:
       retstring += self.__trans.readAll(reqlen - len(retstring))
 
-    self.__rbuf = StringIO(retstring)
+    self.__rbuf = BufferIO(retstring)
     return self.__rbuf
 
 
 class TMemoryBuffer(TTransportBase, CReadableTransport):
-  """Wraps a cStringIO object as a TTransport.
+  """Wraps a cBytesIO object as a TTransport.
 
   NOTE: Unlike the C++ version of this class, you cannot write to it
         then immediately read from it.  If you want to read from a
@@ -208,9 +208,9 @@ class TMemoryBuffer(TTransportBase, CReadableTransport):
     If value is set, this will be a transport for reading,
     otherwise, it is for writing"""
     if value is not None:
-      self._buffer = StringIO(value)
+      self._buffer = BufferIO(value)
     else:
-      self._buffer = StringIO()
+      self._buffer = BufferIO()
 
   def isOpen(self):
     return not self._buffer.closed
@@ -256,8 +256,8 @@ class TFramedTransport(TTransportBase, CReadableTransport):
 
   def __init__(self, trans,):
     self.__trans = trans
-    self.__rbuf = StringIO()
-    self.__wbuf = StringIO()
+    self.__rbuf = BufferIO()
+    self.__wbuf = BufferIO()
 
   def isOpen(self):
     return self.__trans.isOpen()
@@ -279,7 +279,7 @@ class TFramedTransport(TTransportBase, CReadableTransport):
   def readFrame(self):
     buff = self.__trans.readAll(4)
     sz, = unpack('!i', buff)
-    self.__rbuf = StringIO(self.__trans.readAll(sz))
+    self.__rbuf = BufferIO(self.__trans.readAll(sz))
 
   def write(self, buf):
     self.__wbuf.write(buf)
@@ -288,7 +288,7 @@ class TFramedTransport(TTransportBase, CReadableTransport):
     wout = self.__wbuf.getvalue()
     wsz = len(wout)
     # reset wbuf before write/flush to preserve state on underlying failure
-    self.__wbuf = StringIO()
+    self.__wbuf = BufferIO()
     # N.B.: Doing this string concatenation is WAY cheaper than making
     # two separate calls to the underlying socket object. Socket writes in
     # Python turn out to be REALLY expensive, but it seems to do a pretty
@@ -309,7 +309,7 @@ class TFramedTransport(TTransportBase, CReadableTransport):
     while len(prefix) < reqlen:
       self.readFrame()
       prefix += self.__rbuf.getvalue()
-    self.__rbuf = StringIO(prefix)
+    self.__rbuf = BufferIO(prefix)
     return self.__rbuf
 
 
@@ -337,7 +337,7 @@ class TFileObjectTransport(TTransportBase):
 
 class TSaslClientTransport(TTransportBase, CReadableTransport):
   """
-  SASL transport 
+  SASL transport
   """
 
   START = 1
@@ -363,8 +363,8 @@ class TSaslClientTransport(TTransportBase, CReadableTransport):
     self.transport = transport
     self.sasl = SASLClient(host, service, mechanism, **sasl_kwargs)
 
-    self.__wbuf = StringIO()
-    self.__rbuf = StringIO()
+    self.__wbuf = BufferIO()
+    self.__rbuf = BufferIO()
 
   def open(self):
     if not self.transport.isOpen():
@@ -409,7 +409,7 @@ class TSaslClientTransport(TTransportBase, CReadableTransport):
     encoded = self.sasl.wrap(data)
     self.transport.write(''.join((pack("!i", len(encoded)), encoded)))
     self.transport.flush()
-    self.__wbuf = StringIO()
+    self.__wbuf = BufferIO()
 
   def read(self, sz):
     ret = self.__rbuf.read(sz)
@@ -423,7 +423,7 @@ class TSaslClientTransport(TTransportBase, CReadableTransport):
     header = self.transport.readAll(4)
     length, = unpack('!i', header)
     encoded = self.transport.readAll(length)
-    self.__rbuf = StringIO(self.sasl.unwrap(encoded))
+    self.__rbuf = BufferIO(self.sasl.unwrap(encoded))
 
   def close(self):
     self.sasl.dispose()
@@ -441,6 +441,6 @@ class TSaslClientTransport(TTransportBase, CReadableTransport):
     while len(prefix) < reqlen:
       self._read_frame()
       prefix += self.__rbuf.getvalue()
-    self.__rbuf = StringIO(prefix)
+    self.__rbuf = BufferIO(prefix)
     return self.__rbuf
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/transport/TTwisted.py
----------------------------------------------------------------------
diff --git a/lib/py/src/transport/TTwisted.py b/lib/py/src/transport/TTwisted.py
index 29bbd4c..cabe345 100644
--- a/lib/py/src/transport/TTwisted.py
+++ b/lib/py/src/transport/TTwisted.py
@@ -17,8 +17,8 @@
 # under the License.
 #
 
+from io import BytesIO
 import struct
-from cStringIO import StringIO
 
 from zope.interface import implements, Interface, Attribute
 from twisted.internet.protocol import ServerFactory, ClientFactory, \
@@ -29,19 +29,20 @@ from twisted.protocols import basic
 from twisted.web import server, resource, http
 
 from thrift.transport import TTransport
+import six
 
 
 class TMessageSenderTransport(TTransport.TTransportBase):
 
     def __init__(self):
-        self.__wbuf = StringIO()
+        self.__wbuf = BytesIO()
 
     def write(self, buf):
         self.__wbuf.write(buf)
 
     def flush(self):
         msg = self.__wbuf.getvalue()
-        self.__wbuf = StringIO()
+        self.__wbuf = BytesIO()
         return self.sendMessage(msg)
 
     def sendMessage(self, message):
@@ -82,7 +83,7 @@ class ThriftClientProtocol(basic.Int32StringReceiver):
         self.started.callback(self.client)
 
     def connectionLost(self, reason=connectionDone):
-        for k, v in self.client._reqs.iteritems():
+        for k, v in six.iteritems(self.client._reqs):
             tex = TTransport.TTransportException(
                 type=TTransport.TTransportException.END_OF_FILE,
                 message='Connection closed')

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/lib/py/src/transport/TZlibTransport.py
----------------------------------------------------------------------
diff --git a/lib/py/src/transport/TZlibTransport.py b/lib/py/src/transport/TZlibTransport.py
index a2f42a5..7fe5853 100644
--- a/lib/py/src/transport/TZlibTransport.py
+++ b/lib/py/src/transport/TZlibTransport.py
@@ -24,8 +24,8 @@ data compression.
 
 from __future__ import division
 import zlib
-from cStringIO import StringIO
-from TTransport import TTransportBase, CReadableTransport
+from .TTransport import TTransportBase, CReadableTransport
+from ..compat import BufferIO
 
 
 class TZlibTransportFactory(object):
@@ -88,8 +88,8 @@ class TZlibTransport(TTransportBase, CReadableTransport):
     """
     self.__trans = trans
     self.compresslevel = compresslevel
-    self.__rbuf = StringIO()
-    self.__wbuf = StringIO()
+    self.__rbuf = BufferIO()
+    self.__wbuf = BufferIO()
     self._init_zlib()
     self._init_stats()
 
@@ -97,8 +97,8 @@ class TZlibTransport(TTransportBase, CReadableTransport):
     """Internal method to initialize/reset the internal StringIO objects
     for read and write buffers.
     """
-    self.__rbuf = StringIO()
-    self.__wbuf = StringIO()
+    self.__rbuf = BufferIO()
+    self.__wbuf = BufferIO()
 
   def _init_stats(self):
     """Internal method to reset the internal statistics counters
@@ -203,7 +203,7 @@ class TZlibTransport(TTransportBase, CReadableTransport):
     self.bytes_in += len(zbuf)
     self.bytes_in_comp += len(buf)
     old = self.__rbuf.read()
-    self.__rbuf = StringIO(old + buf)
+    self.__rbuf = BufferIO(old + buf)
     if len(old) + len(buf) == 0:
       return False
     return True
@@ -228,7 +228,7 @@ class TZlibTransport(TTransportBase, CReadableTransport):
     ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH)
     self.bytes_out_comp += len(ztail)
     if (len(zbuf) + len(ztail)) > 0:
-      self.__wbuf = StringIO()
+      self.__wbuf = BufferIO()
       self.__trans.write(zbuf + ztail)
     self.__trans.flush()
 
@@ -244,5 +244,5 @@ class TZlibTransport(TTransportBase, CReadableTransport):
       retstring += self.read(self.DEFAULT_BUFFSIZE)
     while len(retstring) < reqlen:
       retstring += self.read(reqlen - len(retstring))
-    self.__rbuf = StringIO(retstring)
+    self.__rbuf = BufferIO(retstring)
     return self.__rbuf

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py.tornado/test_suite.py
----------------------------------------------------------------------
diff --git a/test/py.tornado/test_suite.py b/test/py.tornado/test_suite.py
index b63ea2d..e0bf913 100755
--- a/test/py.tornado/test_suite.py
+++ b/test/py.tornado/test_suite.py
@@ -28,12 +28,12 @@ import unittest
 
 basepath = os.path.abspath(os.path.dirname(__file__))
 sys.path.insert(0, basepath+'/gen-py.tornado')
-sys.path.insert(0, glob.glob(os.path.join(basepath, '../../lib/py/build/lib.*'))[0])
+sys.path.insert(0, glob.glob(os.path.join(basepath, '../../lib/py/build/lib*'))[0])
 
 try:
     __import__('tornado')
 except ImportError:
-    print "module `tornado` not found, skipping test"
+    print("module `tornado` not found, skipping test")
     sys.exit(0)
 
 from tornado import gen
@@ -188,9 +188,9 @@ class ThriftTestCase(AsyncTestCase):
         self.assertEqual(y.i64_thing, -5)
 
     def test_oneway(self):
-        self.client.testOneway(0.5)
+        self.client.testOneway(0)
         start, end, seconds = self.wait(timeout=1)
-        self.assertAlmostEquals(seconds, (end - start), places=3)
+        self.assertAlmostEqual(seconds, (end - start), places=3)
 
     @gen_test
     def test_map(self):

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py.twisted/test_suite.py
----------------------------------------------------------------------
diff --git a/test/py.twisted/test_suite.py b/test/py.twisted/test_suite.py
index 048abc5..2c07baa 100755
--- a/test/py.twisted/test_suite.py
+++ b/test/py.twisted/test_suite.py
@@ -172,7 +172,7 @@ class ThriftTestCase(unittest.TestCase):
         try:
             yield self.client.testException('Xception')
             self.fail("should have gotten exception")
-        except Xception, x:
+        except Xception as x:
             self.assertEquals(x.errorCode, 1001)
             self.assertEquals(x.message, 'Xception')
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/RunClientServer.py
----------------------------------------------------------------------
diff --git a/test/py/RunClientServer.py b/test/py/RunClientServer.py
index b7f4332..7224bac 100755
--- a/test/py/RunClientServer.py
+++ b/test/py/RunClientServer.py
@@ -20,6 +20,7 @@
 #
 
 from __future__ import division
+from __future__ import print_function
 import time
 import socket
 import subprocess
@@ -77,13 +78,13 @@ SERVERS = [
 try:
   import multiprocessing
 except:
-  print 'Warning: the multiprocessing module is unavailable. Skipping tests for TProcessPoolServer'
+  print('Warning: the multiprocessing module is unavailable. Skipping tests for TProcessPoolServer')
   SERVERS.remove('TProcessPoolServer')
 
 try:
   import ssl
 except:
-  print 'Warning, no ssl module available. Skipping all SSL tests.'
+  print('Warning, no ssl module available. Skipping all SSL tests.')
   SKIP_SSL.extend(SERVERS)
 
 # commandline permits a single class name to be specified to override SERVERS=[...]
@@ -91,7 +92,7 @@ if len(args) == 1:
   if args[0] in SERVERS:
     SERVERS = args
   else:
-    print 'Unavailable server type "%s", please choose one of: %s' % (args[0], SERVERS)
+    print('Unavailable server type "%s", please choose one of: %s' % (args[0], SERVERS))
     sys.exit(0)
 
 
@@ -102,7 +103,7 @@ def runScriptTest(genpydir, script):
   script_args = [sys.executable, relfile(script) ]
   script_args.append('--genpydir=%s' % genpydir)
   serverproc = subprocess.Popen(script_args)
-  print '\nTesting script: %s\n----' % (' '.join(script_args))
+  print('\nTesting script: %s\n----' % (' '.join(script_args)))
   ret = subprocess.call(script_args)
   if ret != 0:
     raise Exception("Script subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(script_args)))
@@ -133,12 +134,12 @@ def runServiceTest(genpydir, server_class, proto, port, use_zlib, use_ssl):
   if server_class == 'THttpServer':
     cli_args.append('--http=/')
   if options.verbose > 0:
-    print 'Testing server %s: %s' % (server_class, ' '.join(server_args))
+    print('Testing server %s: %s' % (server_class, ' '.join(server_args)))
   serverproc = subprocess.Popen(server_args)
 
   def ensureServerAlive():
     if serverproc.poll() is not None:
-      print ('FAIL: Server process (%s) failed with retcode %d'
+      print(('FAIL: Server process (%s) failed with retcode %d')
              % (' '.join(server_args), serverproc.returncode))
       raise Exception('Server subprocess %s died, args: %s'
                       % (server_class, ' '.join(server_args)))
@@ -161,7 +162,7 @@ def runServiceTest(genpydir, server_class, proto, port, use_zlib, use_ssl):
 
   try:
     if options.verbose > 0:
-      print 'Testing client: %s' % (' '.join(cli_args))
+      print('Testing client: %s' % (' '.join(cli_args)))
     ret = subprocess.call(cli_args)
     if ret != 0:
       raise Exception("Client subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(cli_args)))
@@ -170,7 +171,7 @@ def runServiceTest(genpydir, server_class, proto, port, use_zlib, use_ssl):
     ensureServerAlive()
     extra_sleep = EXTRA_DELAY.get(server_class, 0)
     if extra_sleep > 0 and options.verbose > 0:
-      print ('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child'
+      print('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child'
              'processes to terminate via alarm'
              % (server_class, proto, use_zlib, use_ssl, extra_sleep))
       time.sleep(extra_sleep)
@@ -179,22 +180,22 @@ def runServiceTest(genpydir, server_class, proto, port, use_zlib, use_ssl):
 
 test_count = 0
 # run tests without a client/server first
-print '----------------'
-print ' Executing individual test scripts with various generated code directories'
-print ' Directories to be tested: ' + ', '.join(generated_dirs)
-print ' Scripts to be tested: ' + ', '.join(SCRIPTS)
-print '----------------'
+print('----------------')
+print(' Executing individual test scripts with various generated code directories')
+print(' Directories to be tested: ' + ', '.join(generated_dirs))
+print(' Scripts to be tested: ' + ', '.join(SCRIPTS))
+print('----------------')
 for genpydir in generated_dirs:
   for script in SCRIPTS:
     runScriptTest(genpydir, script)
 
-print '----------------'
-print ' Executing Client/Server tests with various generated code directories'
-print ' Servers to be tested: ' + ', '.join(SERVERS)
-print ' Directories to be tested: ' + ', '.join(generated_dirs)
-print ' Protocols to be tested: ' + ', '.join(PROTOS)
-print ' Options to be tested: ZLIB(yes/no), SSL(yes/no)'
-print '----------------'
+print('----------------')
+print(' Executing Client/Server tests with various generated code directories')
+print(' Servers to be tested: ' + ', '.join(SERVERS))
+print(' Directories to be tested: ' + ', '.join(generated_dirs))
+print(' Protocols to be tested: ' + ', '.join(PROTOS))
+print(' Options to be tested: ZLIB(yes/no), SSL(yes/no)')
+print('----------------')
 for try_server in SERVERS:
   for genpydir in generated_dirs:
     for try_proto in PROTOS:
@@ -208,7 +209,7 @@ for try_server in SERVERS:
             continue
           test_count += 1
           if options.verbose > 0:
-            print '\nTest run #%d:  (includes %s) Server=%s,  Proto=%s,  zlib=%s,  SSL=%s' % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl)
+            print('\nTest run #%d:  (includes %s) Server=%s,  Proto=%s,  zlib=%s,  SSL=%s' % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
           runServiceTest(genpydir, try_server, try_proto, options.port, with_zlib, with_ssl)
           if options.verbose > 0:
-            print 'OK: Finished (includes %s)  %s / %s proto / zlib=%s / SSL=%s.   %d combinations tested.' % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count)
+            print('OK: Finished (includes %s)  %s / %s proto / zlib=%s / SSL=%s.   %d combinations tested.' % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/SerializationTest.py
----------------------------------------------------------------------
diff --git a/test/py/SerializationTest.py b/test/py/SerializationTest.py
index 40a52e6..29f3656 100755
--- a/test/py/SerializationTest.py
+++ b/test/py/SerializationTest.py
@@ -26,7 +26,7 @@ parser.add_option('--genpydir', type='string', dest='genpydir', default='gen-py'
 options, args = parser.parse_args()
 del sys.argv[1:] # clean up hack so unittest doesn't complain
 sys.path.insert(0, options.genpydir)
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from ThriftTest.ttypes import *
 from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty
@@ -305,7 +305,7 @@ class AcceleratedFramedTest(unittest.TestCase):
     prot.writeString(bigstring)
     prot.writeI16(24)
     data = databuf.getvalue()
-    cutpoint = len(data)/2
+    cutpoint = len(data) // 2
     parts = [ data[:cutpoint], data[cutpoint:] ]
 
     framed_buffer = TTransport.TMemoryBuffer()
@@ -346,14 +346,14 @@ class SerializersTest(unittest.TestCase):
     objcopy = Bools()
     deserialize(objcopy, serialize(obj))
     self.assertEquals(obj, objcopy)
-    
+
     # test enums
-    for num, name in Numberz._VALUES_TO_NAMES.iteritems():
+    for num, name in Numberz._VALUES_TO_NAMES.items():
       obj = Bonk(message='enum Numberz value %d is string %s' % (num, name), type=num)
       objcopy = Bonk()
       deserialize(objcopy, serialize(obj))
       self.assertEquals(obj, objcopy)
-  
+
 
 def suite():
   suite = unittest.TestSuite()

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TSimpleJSONProtocolTest.py
----------------------------------------------------------------------
diff --git a/test/py/TSimpleJSONProtocolTest.py b/test/py/TSimpleJSONProtocolTest.py
index 080293a..b8db932 100644
--- a/test/py/TSimpleJSONProtocolTest.py
+++ b/test/py/TSimpleJSONProtocolTest.py
@@ -27,7 +27,7 @@ parser.add_option('--genpydir', type='string', dest='genpydir', default='gen-py'
 options, args = parser.parse_args()
 del sys.argv[1:] # clean up hack so unittest doesn't complain
 sys.path.insert(0, options.genpydir)
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from ThriftTest.ttypes import *
 from thrift.protocol import TJSONProtocol
@@ -45,7 +45,7 @@ class SimpleJSONProtocolTest(unittest.TestCase):
       # assertDictEqual only in Python 2.7. Depends on your machine.
       self.assertDictEqual(a, b, msg)
       return
-    
+
     # Substitute implementation not as good as unittest library's
     self.assertEquals(len(a), len(b), msg)
     for k, v in a.iteritems():
@@ -66,7 +66,7 @@ class SimpleJSONProtocolTest(unittest.TestCase):
 
   def testWriteOnly(self):
     self.assertRaises(NotImplementedError,
-                      self._deserialize, VersioningTestV1, '{}')
+                      self._deserialize, VersioningTestV1, b'{}')
 
   def testSimpleMessage(self):
       v1obj = VersioningTestV1(
@@ -76,10 +76,10 @@ class SimpleJSONProtocolTest(unittest.TestCase):
       expected = dict(begin_in_both=v1obj.begin_in_both,
                       old_string=v1obj.old_string,
                       end_in_both=v1obj.end_in_both)
-      actual = json.loads(self._serialize(v1obj))
+      actual = json.loads(self._serialize(v1obj).decode('ascii'))
 
       self._assertDictEqual(expected, actual)
-     
+
   def testComplicated(self):
       v2obj = VersioningTestV2(
           begin_in_both=12345,
@@ -107,10 +107,10 @@ class SimpleJSONProtocolTest(unittest.TestCase):
                       newmap=v2obj.newmap,
                       newstring=v2obj.newstring,
                       end_in_both=v2obj.end_in_both)
-      
+
       # Need to load/dump because map keys get escaped.
       expected = json.loads(json.dumps(expected))
-      actual = json.loads(self._serialize(v2obj))
+      actual = json.loads(self._serialize(v2obj).decode('ascii'))
       self._assertDictEqual(expected, actual)
 
 

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TestClient.py
----------------------------------------------------------------------
diff --git a/test/py/TestClient.py b/test/py/TestClient.py
index 3a74353..4689d63 100755
--- a/test/py/TestClient.py
+++ b/test/py/TestClient.py
@@ -54,11 +54,11 @@ parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', p
 options, args = parser.parse_args()
 
 script_dir = os.path.abspath(os.path.dirname(__file__))
-lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib.*')
+lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib*')
 sys.path.insert(0, os.path.join(script_dir, options.genpydir))
 sys.path.insert(0, glob.glob(lib_dir)[0])
 
-from ThriftTest import ThriftTest, SecondService
+from ThriftTest import ThriftTest
 from ThriftTest.ttypes import *
 from thrift.transport import TTransport
 from thrift.transport import TSocket
@@ -105,8 +105,8 @@ class AbstractTest(unittest.TestCase):
     print('testString')
     self.assertEqual(self.client.testString('Python' * 20), 'Python' * 20)
     self.assertEqual(self.client.testString(''), '')
-    self.assertEqual(self.client.testString(u'パイソン'.encode('utf8')), u'パイソン'.encode('utf8'))
-    s = u"""Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
+    s1 = u'\b\t\n/\\\\\r{}:パイソン"'
+    s2 = u"""Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
         Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška,
         Беларуская, Беларуская (тарашкевіца), Български, Bamanankan,
         বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн,
@@ -131,7 +131,11 @@ class AbstractTest(unittest.TestCase):
         Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
         Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
         Bân-lâm-gú, 粵語"""
-    self.assertEqual(self.client.testString(s.encode('utf8')), s.encode('utf8'))
+    if sys.version_info[0] == 2:
+      s1 = s1.encode('utf8')
+      s2 = s2.encode('utf8')
+    self.assertEqual(self.client.testString(s1), s1)
+    self.assertEqual(self.client.testString(s2), s2)
 
   def testBool(self):
     print('testBool')
@@ -290,19 +294,23 @@ class AbstractTest(unittest.TestCase):
 class NormalBinaryTest(AbstractTest):
   protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()
 
+
 class CompactTest(AbstractTest):
   protocol_factory = TCompactProtocol.TCompactProtocolFactory()
 
+
 class JSONTest(AbstractTest):
   protocol_factory = TJSONProtocol.TJSONProtocolFactory()
 
+
 class AcceleratedBinaryTest(AbstractTest):
   protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory()
 
+
 def suite():
   suite = unittest.TestSuite()
   loader = unittest.TestLoader()
-  if options.proto == 'binary': # look for --proto on cmdline
+  if options.proto == 'binary':  # look for --proto on cmdline
     suite.addTest(loader.loadTestsFromTestCase(NormalBinaryTest))
   elif options.proto == 'accel':
     suite.addTest(loader.loadTestsFromTestCase(AcceleratedBinaryTest))
@@ -314,6 +322,7 @@ def suite():
     raise AssertionError('Unknown protocol given with --protocol: %s' % options.proto)
   return suite
 
+
 class OwnArgsTestProgram(unittest.TestProgram):
     def parseArgs(self, argv):
         if args:

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TestEof.py
----------------------------------------------------------------------
diff --git a/test/py/TestEof.py b/test/py/TestEof.py
index a9d81f1..7677de8 100755
--- a/test/py/TestEof.py
+++ b/test/py/TestEof.py
@@ -26,7 +26,7 @@ parser.add_option('--genpydir', type='string', dest='genpydir', default='gen-py'
 options, args = parser.parse_args()
 del sys.argv[1:] # clean up hack so unittest doesn't complain
 sys.path.insert(0, options.genpydir)
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from ThriftTest import ThriftTest
 from ThriftTest.ttypes import *
@@ -99,7 +99,7 @@ class TestEof(unittest.TestCase):
     # TODO: we should make sure this covers more of the code paths
 
     data = self.make_data(pfactory)
-    for i in xrange(0, len(data) + 1):
+    for i in range(0, len(data) + 1):
       trans = TTransport.TMemoryBuffer(data[0:i])
       prot = pfactory.getProtocol(trans)
       try:

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TestServer.py
----------------------------------------------------------------------
diff --git a/test/py/TestServer.py b/test/py/TestServer.py
index e7478e4..bc221c0 100755
--- a/test/py/TestServer.py
+++ b/test/py/TestServer.py
@@ -18,7 +18,7 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-from __future__ import division
+from __future__ import division, print_function
 import glob
 import logging
 import os
@@ -53,7 +53,7 @@ parser.set_defaults(port=9090, verbose=1, proto='binary')
 options, args = parser.parse_args()
 
 script_dir = os.path.realpath(os.path.dirname(__file__))  # <-- absolute dir the script is in
-lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib.*')
+lib_dir = os.path.join(os.path.dirname(os.path.dirname(script_dir)), 'lib', 'py', 'build', 'lib*')
 
 sys.path.insert(0, os.path.join(script_dir, options.genpydir))
 sys.path.insert(0, glob.glob(lib_dir)[0])

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TestSocket.py
----------------------------------------------------------------------
diff --git a/test/py/TestSocket.py b/test/py/TestSocket.py
index b9bdf27..55e4996 100755
--- a/test/py/TestSocket.py
+++ b/test/py/TestSocket.py
@@ -26,7 +26,7 @@ parser.add_option('--genpydir', type='string', dest='genpydir', default='gen-py'
 options, args = parser.parse_args()
 del sys.argv[1:] # clean up hack so unittest doesn't complain
 sys.path.insert(0, options.genpydir)
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from ThriftTest import ThriftTest
 from ThriftTest.ttypes import *
@@ -41,7 +41,7 @@ from optparse import OptionParser
 
 class TimeoutTest(unittest.TestCase):
     def setUp(self):
-        for i in xrange(50):
+        for i in range(50):
             try:
                 # find a port we can use
                 self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -58,7 +58,7 @@ class TimeoutTest(unittest.TestCase):
 
         try:
             leaky = []
-            for i in xrange(100):
+            for i in range(100):
                 socket = TSocket.TSocket('localhost', self.port)
                 socket.setTimeout(10)
                 socket.open()

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/test/py/TestSyntax.py
----------------------------------------------------------------------
diff --git a/test/py/TestSyntax.py b/test/py/TestSyntax.py
index 9f71cf5..cdf0e0d 100755
--- a/test/py/TestSyntax.py
+++ b/test/py/TestSyntax.py
@@ -26,7 +26,7 @@ parser.add_option('--genpydir', type='string', dest='genpydir', default='gen-py'
 options, args = parser.parse_args()
 del sys.argv[1:] # clean up hack so unittest doesn't complain
 sys.path.insert(0, options.genpydir)
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 # Just import these generated files to make sure they are syntactically valid
 from DebugProtoTest import EmptyService

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py.tornado/PythonClient.py
----------------------------------------------------------------------
diff --git a/tutorial/py.tornado/PythonClient.py b/tutorial/py.tornado/PythonClient.py
index 95d78b8..005ad3c 100755
--- a/tutorial/py.tornado/PythonClient.py
+++ b/tutorial/py.tornado/PythonClient.py
@@ -22,7 +22,7 @@
 import sys
 import glob
 sys.path.append('gen-py.tornado')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 import logging
 
@@ -56,21 +56,21 @@ def communicate(callback=None):
 
     # ping
     yield gen.Task(client.ping)
-    print "ping()"
+    print("ping()")
 
     # add
     sum_ = yield gen.Task(client.add, 1, 1)
-    print "1 + 1 = {}".format(sum_)
+    print("1 + 1 = {}".format(sum_))
 
     # make a oneway call without a callback (schedule the write and continue
     # without blocking)
     client.zip()
-    print "zip() without callback"
+    print("zip() without callback")
 
     # make a oneway call with a callback (we'll wait for the stream write to
     # complete before continuing)
     yield gen.Task(client.zip)
-    print "zip() with callback"
+    print("zip() with callback")
 
     # calculate 1/0
     work = Work()
@@ -80,9 +80,9 @@ def communicate(callback=None):
 
     try:
         quotient = yield gen.Task(client.calculate, 1, work)
-        print "Whoa? You know how to divide by zero?"
+        print("Whoa? You know how to divide by zero?")
     except InvalidOperation as io:
-        print "InvalidOperation: {}".format(io)
+        print("InvalidOperation: {}".format(io))
 
     # calculate 15-10
     work.op = Operation.SUBTRACT
@@ -90,11 +90,11 @@ def communicate(callback=None):
     work.num2 = 10
 
     diff = yield gen.Task(client.calculate, 1, work)
-    print "15 - 10 = {}".format(diff)
+    print("15 - 10 = {}".format(diff))
 
     # getStruct
     log = yield gen.Task(client.getStruct, 1)
-    print "Check log: {}".format(log.value)
+    print("Check log: {}".format(log.value))
 
     # close the transport
     client._transport.close()

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py.tornado/PythonServer.py
----------------------------------------------------------------------
diff --git a/tutorial/py.tornado/PythonServer.py b/tutorial/py.tornado/PythonServer.py
index 7a34107..4198214 100755
--- a/tutorial/py.tornado/PythonServer.py
+++ b/tutorial/py.tornado/PythonServer.py
@@ -22,7 +22,7 @@
 import sys
 import glob
 sys.path.append('gen-py.tornado')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from tutorial import Calculator
 from tutorial.ttypes import Operation, InvalidOperation
@@ -43,15 +43,15 @@ class CalculatorHandler(object):
         self.log = {}
 
     def ping(self, callback):
-        print "ping()"
+        print("ping()")
         callback()
 
     def add(self, n1, n2, callback):
-        print "add({}, {})".format(n1, n2)
+        print("add({}, {})".format(n1, n2))
         callback(n1 + n2)
 
     def calculate(self, logid, work, callback):
-        print "calculate({}, {})".format(logid, work)
+        print("calculate({}, {})".format(logid, work))
 
         if work.op == Operation.ADD:
             val = work.num1 + work.num2
@@ -79,11 +79,11 @@ class CalculatorHandler(object):
         callback(val)
 
     def getStruct(self, key, callback):
-        print "getStruct({})".format(key)
+        print("getStruct({})".format(key))
         callback(self.log[key])
 
     def zip(self, callback):
-        print "zip()"
+        print("zip()")
         callback()
 
 
@@ -93,11 +93,11 @@ def main():
     pfactory = TBinaryProtocol.TBinaryProtocolFactory()
     server = TTornado.TTornadoServer(processor, pfactory)
 
-    print "Starting the server..."
+    print("Starting the server...")
     server.bind(9090)
     server.start(1)
     ioloop.IOLoop.instance().start()
-    print "done."
+    print("done.")
 
 
 if __name__ == "__main__":

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py.twisted/PythonClient.py
----------------------------------------------------------------------
diff --git a/tutorial/py.twisted/PythonClient.py b/tutorial/py.twisted/PythonClient.py
index 9e086f0..e80c0fc 100755
--- a/tutorial/py.twisted/PythonClient.py
+++ b/tutorial/py.twisted/PythonClient.py
@@ -21,7 +21,7 @@
 
 import sys, glob
 sys.path.append('gen-py.twisted')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from tutorial import Calculator
 from tutorial.ttypes import *
@@ -37,10 +37,10 @@ from thrift.protocol import TBinaryProtocol
 @inlineCallbacks
 def main(client):
   yield client.ping()
-  print 'ping()'
+  print('ping()')
 
   sum = yield client.add(1,1)
-  print '1+1=%d' % (sum)
+  print(('1+1=%d' % (sum)))
 
   work = Work()
 
@@ -50,19 +50,19 @@ def main(client):
 
   try:
     quotient = yield client.calculate(1, work)
-    print 'Whoa? You know how to divide by zero?'
-  except InvalidOperation, io:
-    print 'InvalidOperation: %r' % io
+    print('Whoa? You know how to divide by zero?')
+  except InvalidOperation as e:
+    print(('InvalidOperation: %r' % e))
 
   work.op = Operation.SUBTRACT
   work.num1 = 15
   work.num2 = 10
 
   diff = yield client.calculate(1, work)
-  print '15-10=%d' % (diff)
+  print(('15-10=%d' % (diff)))
 
   log = yield client.getStruct(1)
-  print 'Check log: %s' % (log.value)
+  print(('Check log: %s' % (log.value)))
   reactor.stop()
 
 if __name__ == '__main__':

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py.twisted/PythonServer.py
----------------------------------------------------------------------
diff --git a/tutorial/py.twisted/PythonServer.py b/tutorial/py.twisted/PythonServer.py
index 227f6d4..c578321 100755
--- a/tutorial/py.twisted/PythonServer.py
+++ b/tutorial/py.twisted/PythonServer.py
@@ -21,7 +21,7 @@
 
 import sys, glob
 sys.path.append('gen-py.twisted')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from tutorial import Calculator
 from tutorial.ttypes import *
@@ -36,19 +36,19 @@ from thrift.protocol import TBinaryProtocol
 from thrift.server import TServer
 
 class CalculatorHandler:
-  implements(Calculator.Iface)  
+  implements(Calculator.Iface)
   def __init__(self):
     self.log = {}
 
   def ping(self):
-    print 'ping()'
+    print('ping()')
 
   def add(self, n1, n2):
-    print 'add(%d,%d)' % (n1, n2)
+    print('add(%d,%d)' % (n1, n2))
     return n1+n2
 
   def calculate(self, logid, work):
-    print 'calculate(%d, %r)' % (logid, work)
+    print('calculate(%d, %r)' % (logid, work))
 
     if work.op == Operation.ADD:
       val = work.num1 + work.num2
@@ -77,11 +77,11 @@ class CalculatorHandler:
     return val
 
   def getStruct(self, key):
-    print 'getStruct(%d)' % (key)
+    print('getStruct(%d)' % (key))
     return self.log[key]
 
   def zip(self):
-    print 'zip()'
+    print('zip()')
 
 if __name__ == '__main__':
     handler = CalculatorHandler()

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py.twisted/PythonServer.tac
----------------------------------------------------------------------
diff --git a/tutorial/py.twisted/PythonServer.tac b/tutorial/py.twisted/PythonServer.tac
index 1d0b6c4..08493ff 100755
--- a/tutorial/py.twisted/PythonServer.tac
+++ b/tutorial/py.twisted/PythonServer.tac
@@ -24,7 +24,7 @@ from thrift.transport import TTwisted
 
 import sys, glob
 sys.path.append('gen-py.twisted')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 from tutorial import Calculator
 from tutorial.ttypes import *
 from PythonServer import CalculatorHandler

http://git-wip-us.apache.org/repos/asf/thrift/blob/760511f5/tutorial/py/PythonClient.py
----------------------------------------------------------------------
diff --git a/tutorial/py/PythonClient.py b/tutorial/py/PythonClient.py
index 0554ee1..c4559ff 100755
--- a/tutorial/py/PythonClient.py
+++ b/tutorial/py/PythonClient.py
@@ -21,7 +21,7 @@
 
 import sys, glob
 sys.path.append('gen-py')
-sys.path.insert(0, glob.glob('../../lib/py/build/lib.*')[0])
+sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0])
 
 from tutorial import Calculator
 from tutorial.ttypes import *
@@ -49,10 +49,10 @@ try:
   transport.open()
 
   client.ping()
-  print 'ping()'
+  print('ping()')
 
   sum = client.add(1,1)
-  print '1+1=%d' % (sum)
+  print(('1+1=%d' % (sum)))
 
   work = Work()
 
@@ -62,22 +62,22 @@ try:
 
   try:
     quotient = client.calculate(1, work)
-    print 'Whoa? You know how to divide by zero?'
-  except InvalidOperation, io:
-    print 'InvalidOperation: %r' % io
+    print('Whoa? You know how to divide by zero?')
+  except InvalidOperation as e:
+    print(('InvalidOperation: %r' % e))
 
   work.op = Operation.SUBTRACT
   work.num1 = 15
   work.num2 = 10
 
   diff = client.calculate(1, work)
-  print '15-10=%d' % (diff)
+  print(('15-10=%d' % (diff)))
 
   log = client.getStruct(1)
-  print 'Check log: %s' % (log.value)
+  print(('Check log: %s' % (log.value)))
 
   # Close!
   transport.close()
 
-except Thrift.TException, tx:
-  print '%s' % (tx.message)
+except Thrift.TException as tx:
+  print(('%s' % (tx.message)))