Repository: coala/coala

Issue:
linter/strip_ansi: Error when using `use_stderr=True` and `use_stdout=True`
The feature added in https://github.com/coala/coala/issues/5281 has a flaw when using the options `use_stderr=True` and `use_stdout=True` together:
```python
output = tuple(compress(
    output,
    (options['use_stdout'], options['use_stderr'])))
if len(output) == 1:
    output = output[

if options['strip_ansi']:
    output = strip_ansi(output)
```
In that case `output` becomes a tuple and `strip_ansi` will fail:
```
TypeError: expected string or bytes-like object
```


Discussion:

Resolving patch:
diff --git a/coalib/bearlib/abstractions/Linter.py b/coalib/bearlib/abstractions/Linter.py
--- a/coalib/bearlib/abstractions/Linter.py
+++ b/coalib/bearlib/abstractions/Linter.py
@@ -668,10 +668,10 @@ def run(self, filename=None, file=None, **kwargs):
                 output = tuple(compress(
                     output,
                     (options['use_stdout'], options['use_stderr'])))
+                if options['strip_ansi']:
+                    output = tuple(map(strip_ansi, output))
                 if len(output) == 1:
                     output = output[0]
-                if options['strip_ansi']:
-                    output = strip_ansi(output)
                 process_output_kwargs = FunctionMetadata.filter_parameters(
                     self._get_process_output_metadata(), kwargs)
                 return self.process_output(output, filename, file,


Test patch:
diff --git a/tests/bearlib/abstractions/LinterTest.py b/tests/bearlib/abstractions/LinterTest.py
--- a/tests/bearlib/abstractions/LinterTest.py
+++ b/tests/bearlib/abstractions/LinterTest.py
@@ -327,24 +327,35 @@ def process_output(output, filename, file):
             def create_arguments(filename, file, config_file):
                 code = '\n'.join(['import sys',
                                   "blue = '\033[94m'",
-                                  "print(blue + 'Hello blue')"])
+                                  "print(blue + 'Hello blue')",
+                                  "red = '\033[31m'",
+                                  "print(red + 'Hello red', file=sys.stderr)"])
                 return '-c', code
 
-        uut = (linter(sys.executable, use_stdout=True, strip_ansi=True)
-               (TestLinter)
-               (self.section, None))
-        uut.run('', [])
-
-        process_output_mock.assert_called_once_with('Hello blue\n', '', [])
-        process_output_mock.reset_mock()
-
-        uut = (linter(sys.executable, use_stdout=True, strip_ansi=False)
-               (TestLinter)
-               (self.section, None))
-        uut.run('', [])
+        # use_stdout, use_stderr, strip_ansi, expected result
+        scenarios = [
+            (True, False, True, 'Hello blue\n'),
+            (True, False, False, '\033[94mHello blue\n'),
+            (False, True, True, 'Hello red\n'),
+            (False, True, False, '\033[31mHello red\n'),
+            (True, True, True, ('Hello blue\n',
+                                'Hello red\n')),
+            (True, True, False, ('\033[94mHello blue\n',
+                                 '\033[31mHello red\n')),
+        ]
 
-        process_output_mock.assert_called_once_with(
-            '\033[94mHello blue\n', '', [])
+        for use_stdout, use_stderr, strip_ansi, expected_result in scenarios:
+            uut = (linter(sys.executable,
+                          use_stdout=use_stdout,
+                          use_stderr=use_stderr,
+                          strip_ansi=strip_ansi)
+                   (TestLinter)
+                   (self.section, None))
+            uut.run('', [])
+
+            process_output_mock.assert_called_once_with(
+                expected_result, '', [])
+            process_output_mock.reset_mock()
 
     def test_process_output_corrected(self):
         uut = (linter(sys.executable, output_format='corrected')