utils/run_pylint: Return the pylint error code instead of an exception

If a lint error was found, an exception would be thrown. This would
cause the program to end with a return code of 1, and it would print out
the cryptic error "ERROR:root:4" to stderr.

The pylint api specifies that the return code is a bit field:
* 0   no error
* 1   fatal message issued
* 2   error message issued
* 4   warning message issued
* 8   refactor message issued
* 16  convention message issued
* 32  usage error

This change makes it so the correct error code is returned, and the
exception is no longer thrown. The output on stderr was causing problems
when running pylint in sublime text because it expects stderr to be
empty.

BUG=none
TEST=Ran various tests

$ utils/run_pylint.py client/profilers/sar/sar.py
************* Module files.client.profilers.sar.sar
W:  7, 0: Unused import shutil (unused-import)
ERROR:root:4
$ echo $?
1

$ utils/run_pylint.py client/profilers/sar/sar.py
************* Module files.client.profilers.sar.sar
W:  7, 0: Unused import shutil (unused-import)
$ echo $?
4

$ utils/run_pylint.py client/profilers/
...
$ echo $?
20

Change-Id: I9284bd664095290c569fbe345ad95fd6078b47bc
Signed-off-by: Raul E Rangel <[email protected]>
Reviewed-on: https://chromium-review.googlesource.com/1446542
Reviewed-by: Jason Clinton <[email protected]>
diff --git a/utils/run_pylint.py b/utils/run_pylint.py
index 4002b1d..7d10908 100755
--- a/utils/run_pylint.py
+++ b/utils/run_pylint.py
@@ -187,16 +187,17 @@
     @param file_paths: a list of file paths.
     @param base_opts: a list of pylint config options.
 
+    @returns pylint return code
+
     @raises: pylint_error if pylint finds problems with a file
              in this commit.
     """
     if not file_paths:
-        return
+        return 0
 
     pylint_runner = pylint.lint.Run(list(base_opts) + list(file_paths),
                                     exit=False)
-    if pylint_runner.linter.msg_status:
-        raise pylint_error(pylint_runner.linter.msg_status)
+    return pylint_runner.linter.msg_status
 
 
 def should_check_file(file_path):
@@ -219,6 +220,8 @@
 
     @param base_opts: pylint base options.
     @param file_path: path to the file we need to run pylint on.
+
+    @returns pylint return code
     """
     if not isinstance(file_path, basestring):
         raise TypeError('expected a string as filepath, got %s'%
@@ -226,8 +229,10 @@
 
     if should_check_file(file_path):
         pylint_runner = pylint.lint.Run(base_opts + [file_path], exit=False)
-        if pylint_runner.linter.msg_status:
-            pylint_error(pylint_runner.linter.msg_status)
+
+        return pylint_runner.linter.msg_status
+
+    return 0
 
 
 def visit(arg, dirname, filenames):
@@ -239,7 +244,7 @@
     @param filenames: files in dir from os.walk.path
     """
     for filename in filenames:
-        check_file(os.path.join(dirname, filename), arg)
+        arg.append(os.path.join(dirname, filename))
 
 
 def check_dir(dir_path, base_opts):
@@ -248,8 +253,14 @@
 
     @param base_opts: pylint base options.
     @param dir_path: path to directory.
+
+    @returns pylint return code
     """
-    os.path.walk(dir_path, visit, base_opts)
+    files = []
+
+    os.path.walk(dir_path, visit, files)
+
+    return batch_check_files(files, base_opts)
 
 
 def extend_baseopts(base_opts, new_opt):
@@ -327,6 +338,8 @@
                             absolute path.
     @param commit: hash of the commit this upload applies to.
     @param pylint_base_opts: a list of pylint config options.
+
+    @returns pylint return code
     """
     files_to_check = filter(should_check_file, work_tree_files)
 
@@ -345,7 +358,7 @@
         for file_tuple in zip(work_tree_files, temp_files):
             git_show_to_temp_file(commit, *file_tuple)
         # Only check if we successfully git showed all files in the commit.
-        batch_check_files(temp_files, pylint_base_opts)
+        return batch_check_files(temp_files, pylint_base_opts)
     finally:
         if tempdir:
             tempdir.clean()
@@ -415,19 +428,21 @@
         get_cmdline_options(args_list,
                             pylint_base_opts,
                             open(pylint_rc).read())
-        batch_check_files(args_list, pylint_base_opts)
+        return batch_check_files(args_list, pylint_base_opts)
     elif os.environ.get('PRESUBMIT_FILES') is not None:
-        check_committed_files(
+        return check_committed_files(
                               os.environ.get('PRESUBMIT_FILES').split('\n'),
                               os.environ.get('PRESUBMIT_COMMIT'),
                               pylint_base_opts)
     else:
-        check_dir('.', pylint_base_opts)
+        return check_dir('.', pylint_base_opts)
 
 
 if __name__ == '__main__':
     try:
-        main()
+        ret = main()
+
+        sys.exit(ret)
     except pylint_error as e:
         logging.error(e)
         sys.exit(1)