Repository: oppia/oppia

Issue:
Print lint check error messages at the end of the check
Currently, the lint check error messages are printed as and when they are encountered. Ideally, they should also be printed post the completion of the check.
### Current behavior:
The CSS, JS and Python lint checks, which run in parallel, output/print the error messages to the console/terminal as and when they are encountered.

```bash
Starting linter...
Starting CSS, Javascript and Python Linting
----------------------------------------
Total css files:  406
Linting:  core/templates/dev/head/custom_popover_directive.html
Total css files:  1
Linting:  core/templates/dev/head/css/oppia.css
Total js files:  764
Linting:  gulpfile.js
Linting 390 Python files
Linting Python files 1 to 50...
Using config file /home/travis/build/oppia/oppia/.pylintrc
Linting:  core/templates/dev/head/domain/learner_dashboard/learner_dashboard_icons_directive.html
Linting:  assets/constants.js
CSS linting finished.
Linting:  core/templates/dev/head/pages/header_js_libs.html
........
........
Linting:  extensions/objects/templates/ListOfUnicodeStringEditor.js
Linting:  scripts/gulp-start-gae-devserver.js
Js linting finished.
SUCCESS   406 CSS file linted (728.8 secs)
SUCCESS   1 CSS file linted (4.4 secs)
FAILED    2 JavaScript files
SUCCESS   390 Python files linted (988.4 secs)
```
Here we can see that when the check finishes, the message gets somewhat lost and only the number and type of files which have failed is printed.

## Expected behavior:
This is the way we prefer the CSS, JS and Python lint checks to print the error messages, that is, towards the end, when all the three checks are completed:

```bash
.........
.........
Linting:  extensions/objects/templates/ListOfTabsEditor.js
Linting:  extensions/objects/templates/ImageWithRegionsEditor.js
Linting:  extensions/objects/templates/ListOfUnicodeStringEditor.js
Linting:  scripts/gulp-start-gae-devserver.js
Js linting finished.
SUCCESS   406 CSS file linted (728.8 secs)
SUCCESS   1 CSS file linted (4.4 secs)

core/templates/dev/head/components/summary_tile/StorySummaryTileDirective.js
  37:46  error  Unexpected space before function parentheses  space-before-function-paren
✖ 1 problem (1 error, 0 warnings)
  1 error, 0 warnings potentially fixable with the `--fix` option.

core/templates/dev/head/pages/learner_dashboard/LearnerDashboard.js
  45:15  error  Unexpected console statement. no-console

FAILED    2 JavaScript files
SUCCESS   390 Python files linted (988.4 secs)

```

To fix this issue, some statements probably need to be re-ordered in [these functions](https://github.com/oppia/oppia/blob/develop/scripts/pre_commit_linter.py#L352:L507), specifically `stdout.put` and `result.put` statements, in pre_commit_linter.py.



Discussion:
@apb7 Can I get assigned to this?
And can you please elaborate what to do?
Thanks.
I have updated the issue thread. You can go ahead with this. Thanks, @kranthik13! 
@apb7 So I have to print the errors with the summary at the end and not during the lint checking?
Yes, but only for the CSS, JS and Python lint checks which run in parallel (as mentioned in the example above). Thanks!
@apb7 Can you specify how can I get the error test cases to verify whether its working or not?
For testing, I suggest you make some deliberate lint errors across different files in the code base and then run the linter by `bash scripts/pre_commit_linter.py --path=.` and verify that th errors are placed at the bottom.
@apb7 Somebody has already added for .js files?

Sorry, but I think that the error messages for all CSS, JS and Python files are printed as stated above. Can you please explain what you're referring to? Thanks!
@apb7 Can we talk on gitter?
Sure! Feel free to ping me :)
@kranthik13 any updates on this? Thanks!
Un-assigning @kranthik13 due to lack of response. @dawsoneliasen is interested in working on this. Therefore, assigning him. Thanks!
@apb7 this still needs #5790 to be complete before it should be closed, right?
@dawsoneliasen are you working on the remaining parts of this issue?
@jacobdavis11 No, I'm not. I wasn't aware that there were remaining parts of this issue...

Resolving patch:
diff --git a/scripts/pre_commit_linter.py b/scripts/pre_commit_linter.py
--- a/scripts/pre_commit_linter.py
+++ b/scripts/pre_commit_linter.py
@@ -50,8 +50,10 @@
 # Pylint has issues with the import order of argparse.
 # pylint: disable=wrong-import-order
 import HTMLParser
+import StringIO
 import argparse
 import ast
+import contextlib
 import fnmatch
 import multiprocessing
 import os
@@ -384,6 +386,16 @@ def _get_all_files_in_directory(dir_path, excluded_glob_patterns):
     return files_in_directory
 
 
+@contextlib.contextmanager
+def _redirect_stdout(new_target):
+    old_target = sys.stdout
+    sys.stdout = new_target
+    try:
+        yield new_target
+    finally:
+        sys.stdout = old_target
+
+
 def _lint_css_files(
         node_path, stylelint_path, config_path, files_to_lint, stdout, result):
     """Prints a list of lint errors in the given list of CSS files.
@@ -524,18 +536,21 @@ def _lint_py_files(config_pylint, config_pycodestyle, files_to_lint, result):
         print 'Linting Python files %s to %s...' % (
             current_batch_start_index + 1, current_batch_end_index)
 
-        # This line invokes Pylint and prints its output to the console.
-        pylinter = lint.Run(
-            current_files_to_lint + [config_pylint],
-            exit=False).linter
-        # These lines invoke Pycodestyle.
-        style_guide = pycodestyle.StyleGuide(config_file=config_pycodestyle)
-        pycodestyle_report = style_guide.check_files(
-            paths=current_files_to_lint)
-        # This line prints Pycodestyle's output to the console.
-        pycodestyle_report.print_statistics()
+        target_stdout = StringIO.StringIO()
+        with _redirect_stdout(target_stdout):
+            # This line invokes Pylint and prints its output
+            # to the target stdout.
+            pylinter = lint.Run(
+                current_files_to_lint + [config_pylint],
+                exit=False).linter
+            # These lines invoke Pycodestyle and print its output
+            # to the target stdout.
+            style_guide = pycodestyle.StyleGuide(config_file=config_pycodestyle)
+            pycodestyle_report = style_guide.check_files(
+                paths=current_files_to_lint)
 
         if pylinter.msg_status != 0 or pycodestyle_report.get_count() != 0:
+            result.put(target_stdout.getvalue())
             are_there_errors = True
 
         current_batch_start_index = current_batch_end_index


Test patch: