diff --git a/docs/source/user/invocation.rst b/docs/source/user/invocation.rst index 383e93a..d96d0f9 100644 --- a/docs/source/user/invocation.rst +++ b/docs/source/user/invocation.rst @@ -125,6 +125,7 @@ And you should see something like: available to use. (Default: auto) --output-file=OUTPUT_FILE Redirect report to a file. + --tee Write to stdout and output-file. --append-config=APPEND_CONFIG Provide extra config files to parse in addition to the files found by Flake8 by default. These files are the diff --git a/docs/source/user/options.rst b/docs/source/user/options.rst index acaa67c..78bab1c 100644 --- a/docs/source/user/options.rst +++ b/docs/source/user/options.rst @@ -563,6 +563,26 @@ output_file = output.txt +.. option:: --tee + + Also print output to stdout if output-file has ben configured. + + Command-line example: + + .. prompt:: bash + + flake8 --tee --output-file=output.txt dir/ + + This **can** be specified in config files. + + Example config file usage: + + .. code-block:: ini + + output-file = output.txt + tee = True + + .. option:: --append-config= Provide extra config files to parse in after and in addition to the files diff --git a/src/flake8/formatting/base.py b/src/flake8/formatting/base.py index 2ac0ed8..e7a139a 100644 --- a/src/flake8/formatting/base.py +++ b/src/flake8/formatting/base.py @@ -148,7 +148,7 @@ class BaseFormatter(object): """Handle logic of whether to use an output file or print().""" if self.output_fd is not None: self.output_fd.write(output + self.newline) - else: + if self.output_fd is None or self.options.tee: print(output) def write(self, line, source): diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py index c725c38..a94323b 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -26,6 +26,7 @@ def register_default_options(option_manager): - ``--exit-zero`` - ``-j``/``--jobs`` - ``--output-file`` + - ``--tee`` - ``--append-config`` - ``--config`` - ``--isolated`` @@ -171,6 +172,11 @@ def register_default_options(option_manager): help='Redirect report to a file.', ) + add_option( + '--tee', default=False, parse_from_config=True, action='store_true', + help='Write to stdout and output-file.', + ) + # Config file options add_option( diff --git a/tests/unit/test_base_formatter.py b/tests/unit/test_base_formatter.py index f148806..9da8c65 100644 --- a/tests/unit/test_base_formatter.py +++ b/tests/unit/test_base_formatter.py @@ -1,4 +1,5 @@ """Tests for the BaseFormatter object.""" +import io import optparse import mock @@ -11,6 +12,7 @@ from flake8.formatting import base def options(**kwargs): """Create an optparse.Values instance.""" kwargs.setdefault('output_file', None) + kwargs.setdefault('tee', False) return optparse.Values(kwargs) @@ -76,15 +78,22 @@ def test_show_source_updates_physical_line_appropriately(line, column): assert pointer.count(' ') == column -def test_write_uses_an_output_file(): +@pytest.mark.parametrize('tee', [False, True]) +def test_write_uses_an_output_file(tee): """Verify that we use the output file when it's present.""" - line = 'Something to write' - source = 'source' + line = u'Something to write' + source = u'source' filemock = mock.Mock() - formatter = base.BaseFormatter(options()) - formatter.output_fd = filemock - formatter.write(line, source) + with mock.patch('sys.stdout', new_callable=io.StringIO) as stdout: + formatter = base.BaseFormatter(options(tee=tee)) + formatter.output_fd = filemock + formatter.write(line, source) + if tee: + output = line + formatter.newline + source + formatter.newline + else: + output = '' + assert stdout.getvalue() == output assert filemock.write.called is True assert filemock.write.call_count == 2