THRIFT-3691 Run flake8 Python style check on Travis-CI
Client: Build (Python)
Patch: Nobuaki Sukegawa

This closes #907
diff --git a/.travis.yml b/.travis.yml
index 51e9f47..17fc71f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,6 +20,7 @@
 # build Apache Thrift on Travis CI - https://travis-ci.org/
 
 sudo: required
+dist: trusty
 
 services:
   - docker
@@ -148,12 +149,18 @@
   include:
     # QA jobs for code analytics and metrics
     #
-    # static code analysis with cppcheck
+    # C/C++ static code analysis with cppcheck
     # add --error-exitcode=1 to --enable=all as soon as everything is fixed
-    - env: TEST_NAME="cppcheck"
+    #
+    # Python code style check with flake8
+    #
+    # search for TODO etc within source tree
+    # some statistics about the code base
+    # some info about the build machine
+    - env: TEST_NAME="cppcheck, flake8, TODO FIXME HACK, LoC and system info"
       install:
         - sudo apt-get update
-        - sudo apt-get install cppcheck
+        - sudo apt-get install -y cppcheck sloccount python-flake8
       script:
         # Compiler cppcheck (All)
         - cppcheck --force --quiet --inline-suppr --enable=all -j2 compiler/cpp/src
@@ -165,15 +172,16 @@
         - cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 compiler/cpp/src
         - cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 lib/cpp/src lib/cpp/test test/cpp tutorial/cpp
         - cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib
-
-    # search for TODO etc within source tree
-    # some statistics about the code base
-    # some info about the build machine
-    - env:    TEST_NAME="TODO FIXME HACK, LoC and system info"
-      install:
-        - sudo apt-get update
-        - sudo apt-get install sloccount
-      script:
+        # Python code style
+        - flake8 --ignore=E501 lib/py
+        - flake8 tutorial/py
+        - flake8 --ignore=E501 test/py
+        - flake8 test/py.twisted
+        - flake8 test/py.tornado
+        - flake8 --ignore=E501 test/test.py
+        - flake8 --ignore=E501 test/crossrunner
+        - flake8 test/features
+        # TODO etc
         - grep -r TODO *
         - grep -r FIXME *
         - grep -r HACK *
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 7bd716f..89dd9f9 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -356,6 +356,7 @@
   if (filename[0] == '/') {
     // Realpath!
     char rp[THRIFT_PATH_MAX];
+    // cppcheck-suppress uninitvar
     if (saferealpath(filename.c_str(), rp) == NULL) {
       pwarning(0, "Cannot open include file %s\n", filename.c_str());
       return std::string();
@@ -378,6 +379,7 @@
 
       // Realpath!
       char rp[THRIFT_PATH_MAX];
+      // cppcheck-suppress uninitvar
       if (saferealpath(sfilename.c_str(), rp) == NULL) {
         continue;
       }
@@ -1163,6 +1165,7 @@
         }
         char old_thrift_file_rp[THRIFT_PATH_MAX];
 
+        // cppcheck-suppress uninitvar
         if (saferealpath(arg, old_thrift_file_rp) == NULL) {
           failure("Could not open input file with realpath: %s", arg);
         }
@@ -1231,6 +1234,7 @@
       fprintf(stderr, "Missing file name of new thrift file for audit\n");
       usage();
     }
+    // cppcheck-suppress uninitvar
     if (saferealpath(argv[i], new_thrift_file_rp) == NULL) {
       failure("Could not open input file with realpath: %s", argv[i]);
     }
@@ -1256,6 +1260,7 @@
       fprintf(stderr, "Missing file name\n");
       usage();
     }
+    // cppcheck-suppress uninitvar
     if (saferealpath(argv[i], rp) == NULL) {
       failure("Could not open input file with realpath: %s", argv[i]);
     }
diff --git a/lib/py/CMakeLists.txt b/lib/py/CMakeLists.txt
old mode 100755
new mode 100644
diff --git a/lib/py/Makefile.am b/lib/py/Makefile.am
old mode 100755
new mode 100644
diff --git a/lib/py/setup.cfg b/lib/py/setup.cfg
index c6dab40..c9ed0ae 100644
--- a/lib/py/setup.cfg
+++ b/lib/py/setup.cfg
@@ -2,3 +2,5 @@
 optimize = 1
 [metadata]
 description-file = README.md
+[flake8]
+max-line-length = 100
diff --git a/lib/py/src/compat.py b/lib/py/src/compat.py
index 787149a..41bcf35 100644
--- a/lib/py/src/compat.py
+++ b/lib/py/src/compat.py
@@ -31,7 +31,7 @@
 
 else:
 
-    from io import BytesIO as BufferIO
+    from io import BytesIO as BufferIO  # noqa
 
     def binary_to_str(bin_val):
         return bin_val.decode('utf8')
diff --git a/lib/py/test/thrift_json.py b/lib/py/test/thrift_json.py
index 5ba7dd5..fc1e79c 100644
--- a/lib/py/test/thrift_json.py
+++ b/lib/py/test/thrift_json.py
@@ -20,7 +20,7 @@
 import sys
 import unittest
 
-import _import_local_thrift
+import _import_local_thrift  # noqa
 from thrift.protocol.TJSONProtocol import TJSONProtocol
 from thrift.transport import TTransport
 
diff --git a/test/crossrunner/__init__.py b/test/crossrunner/__init__.py
index 49025ed..9d0b83a 100644
--- a/test/crossrunner/__init__.py
+++ b/test/crossrunner/__init__.py
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-from .test import test_name
-from .collect import collect_cross_tests, collect_feature_tests
-from .run import TestDispatcher
-from .report import generate_known_failures, load_known_failures
+from .test import test_name  # noqa
+from .collect import collect_cross_tests, collect_feature_tests  # noqa
+from .run import TestDispatcher  # noqa
+from .report import generate_known_failures, load_known_failures  # noqa
diff --git a/test/crossrunner/run.py b/test/crossrunner/run.py
index 3f9aaa4..f522bb1 100644
--- a/test/crossrunner/run.py
+++ b/test/crossrunner/run.py
@@ -37,6 +37,10 @@
 RESULT_TIMEOUT = 128
 RESULT_ERROR = 64
 
+# globals
+ports = None
+stop = None
+
 
 class ExecutionContext(object):
     def __init__(self, cmd, cwd, env, report):
diff --git a/test/crossrunner/setup.cfg b/test/crossrunner/setup.cfg
new file mode 100644
index 0000000..7da1f96
--- /dev/null
+++ b/test/crossrunner/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/test/features/container_limit.py b/test/features/container_limit.py
index beed0c5..a16e364 100644
--- a/test/features/container_limit.py
+++ b/test/features/container_limit.py
@@ -4,7 +4,7 @@
 import sys
 
 from util import add_common_args, init_protocol
-from local_thrift import thrift
+from local_thrift import thrift  # noqa
 from thrift.Thrift import TMessageType, TType
 
 
diff --git a/test/features/setup.cfg b/test/features/setup.cfg
new file mode 100644
index 0000000..7da1f96
--- /dev/null
+++ b/test/features/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/test/features/string_limit.py b/test/features/string_limit.py
index 3c68b3e..695d965 100644
--- a/test/features/string_limit.py
+++ b/test/features/string_limit.py
@@ -4,7 +4,7 @@
 import sys
 
 from util import add_common_args, init_protocol
-from local_thrift import thrift
+from local_thrift import thrift  # noqa
 from thrift.Thrift import TMessageType, TType
 
 
diff --git a/test/features/theader_binary.py b/test/features/theader_binary.py
index 02e010b..451399a 100644
--- a/test/features/theader_binary.py
+++ b/test/features/theader_binary.py
@@ -5,7 +5,7 @@
 import sys
 
 from util import add_common_args
-from local_thrift import thrift
+from local_thrift import thrift  # noqa
 from thrift.Thrift import TMessageType, TType
 from thrift.transport.TSocket import TSocket
 from thrift.transport.TTransport import TBufferedTransport, TFramedTransport
diff --git a/test/features/util.py b/test/features/util.py
index e4997d0..3abbbbd 100644
--- a/test/features/util.py
+++ b/test/features/util.py
@@ -1,7 +1,7 @@
 import argparse
 import socket
 
-from local_thrift import thrift
+from local_thrift import thrift  # noqa
 from thrift.transport.TSocket import TSocket
 from thrift.transport.TTransport import TBufferedTransport, TFramedTransport
 from thrift.transport.THttpClient import THttpClient
diff --git a/test/py.tornado/setup.cfg b/test/py.tornado/setup.cfg
new file mode 100644
index 0000000..ae587c4
--- /dev/null
+++ b/test/py.tornado/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E402
+max-line-length = 100
diff --git a/test/py.tornado/test_suite.py b/test/py.tornado/test_suite.py
index b9ce781..32d1c2e 100755
--- a/test/py.tornado/test_suite.py
+++ b/test/py.tornado/test_suite.py
@@ -44,7 +44,7 @@
 from thrift.transport.TTransport import TTransportException
 
 from ThriftTest import ThriftTest
-from ThriftTest.ttypes import *
+from ThriftTest.ttypes import Xception, Xtruct
 
 
 class TestHandler(object):
diff --git a/test/py.twisted/setup.cfg b/test/py.twisted/setup.cfg
new file mode 100644
index 0000000..ae587c4
--- /dev/null
+++ b/test/py.twisted/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E402
+max-line-length = 100
diff --git a/test/py.twisted/test_suite.py b/test/py.twisted/test_suite.py
index 3a59bb1..43149a4 100755
--- a/test/py.twisted/test_suite.py
+++ b/test/py.twisted/test_suite.py
@@ -113,9 +113,8 @@
         self.processor = ThriftTest.Processor(self.handler)
         self.pfactory = TBinaryProtocol.TBinaryProtocolFactory()
 
-        self.server = reactor.listenTCP(0,
-                                        TTwisted.ThriftServerFactory(self.processor,
-                                                                     self.pfactory), interface="127.0.0.1")
+        self.server = reactor.listenTCP(
+            0, TTwisted.ThriftServerFactory(self.processor, self.pfactory), interface="127.0.0.1")
 
         self.portNo = self.server.getHost().port
 
diff --git a/test/py/SerializationTest.py b/test/py/SerializationTest.py
index f4f3a4f..efe3c6d 100755
--- a/test/py/SerializationTest.py
+++ b/test/py/SerializationTest.py
@@ -80,7 +80,7 @@
             vertwo2000=VersioningTestV2(newstruct=Bonk(message='World!', type=314)),
             a_set2500=set(['lazy', 'brown', 'cow']),
             vertwo3000=VersioningTestV2(newset=set([2, 3, 5, 7, 11])),
-            big_numbers=[2**8, 2**16, 2**31 - 1, -(2**31 - 1)]
+            big_numbers=[2 ** 8, 2 ** 16, 2 ** 31 - 1, -(2 ** 31 - 1)]
         )
 
         self.compact_struct = CompactProtoTestStruct(
@@ -160,10 +160,9 @@
             # note, the sets below are sets of chars, since the strings are iterated
             map_int_strset={10: set('abc'), 20: set('def'), 30: set('GHI')},
             map_int_strset_list=[
-            {10: set('abc'), 20: set('def'), 30: set('GHI')},
-            {100: set('lmn'), 200: set('opq'), 300: set('RST')},
-            {1000: set('uvw'), 2000: set('wxy'), 3000: set('XYZ')}
-        ]
+                {10: set('abc'), 20: set('def'), 30: set('GHI')},
+                {100: set('lmn'), 200: set('opq'), 300: set('RST')},
+                {1000: set('uvw'), 2000: set('wxy'), 3000: set('XYZ')}]
         )
 
         self.nested_lists_bonk = NestedListsBonk(
diff --git a/test/py/TestClient.py b/test/py/TestClient.py
index 5de9fa3..18ef66b 100755
--- a/test/py/TestClient.py
+++ b/test/py/TestClient.py
@@ -19,7 +19,6 @@
 # under the License.
 #
 
-import glob
 import os
 import sys
 import time
diff --git a/test/py/TestSyntax.py b/test/py/TestSyntax.py
index c83f40e..dbe7975 100755
--- a/test/py/TestSyntax.py
+++ b/test/py/TestSyntax.py
@@ -20,5 +20,5 @@
 #
 
 # Just import these generated files to make sure they are syntactically valid
-from DebugProtoTest import EmptyService
-from DebugProtoTest import Inherited
+from DebugProtoTest import EmptyService  # noqa
+from DebugProtoTest import Inherited  # noqa
diff --git a/test/py/setup.cfg b/test/py/setup.cfg
new file mode 100644
index 0000000..7da1f96
--- /dev/null
+++ b/test/py/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/tutorial/py/PythonClient.py b/tutorial/py/PythonClient.py
index 8000483..c659716 100755
--- a/tutorial/py/PythonClient.py
+++ b/tutorial/py/PythonClient.py
@@ -52,8 +52,8 @@
     client.ping()
     print('ping()')
 
-    sum = client.add(1, 1)
-    print(('1+1=%d' % (sum)))
+    sum_ = client.add(1, 1)
+    print('1+1=%d' % sum_)
 
     work = Work()
 
@@ -66,17 +66,17 @@
         print('Whoa? You know how to divide by zero?')
         print('FYI the answer is %d' % quotient)
     except InvalidOperation as e:
-        print(('InvalidOperation: %r' % 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()
@@ -85,4 +85,4 @@
     try:
         main()
     except Thrift.TException as tx:
-        print(('%s' % (tx.message)))
+        print('%s' % tx.message)
diff --git a/tutorial/py/PythonServer.py b/tutorial/py/PythonServer.py
index a02a525..eb132a1 100755
--- a/tutorial/py/PythonServer.py
+++ b/tutorial/py/PythonServer.py
@@ -92,8 +92,10 @@
     server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
 
     # You could do one of these for a multithreaded server
-    # server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
-    # server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
+    # server = TServer.TThreadedServer(
+    #     processor, transport, tfactory, pfactory)
+    # server = TServer.TThreadPoolServer(
+    #     processor, transport, tfactory, pfactory)
 
     print('Starting the server...')
     server.serve()
diff --git a/tutorial/py/setup.cfg b/tutorial/py/setup.cfg
new file mode 100644
index 0000000..2a7120a
--- /dev/null
+++ b/tutorial/py/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+ignore = E402