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 c139f69..6252c09 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 been 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 97c60e1..32b827a 100644 --- a/src/flake8/formatting/base.py +++ b/src/flake8/formatting/base.py @@ -150,7 +150,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 47fb30c..7e9b79e 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -27,6 +27,7 @@ def register_default_options(option_manager): - ``--exit-zero`` - ``-j``/``--jobs`` - ``--output-file`` + - ``--tee`` - ``--append-config`` - ``--config`` - ``--isolated`` @@ -174,6 +175,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 8a99fa6..bb54358 100644 --- a/tests/unit/test_base_formatter.py +++ b/tests/unit/test_base_formatter.py @@ -11,6 +11,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 +77,26 @@ def test_show_source_updates_physical_line_appropriately(line, column): assert pointer.count(' ') == (column - 1) -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' filemock = mock.Mock() - formatter = base.BaseFormatter(options()) + formatter = base.BaseFormatter(options(tee=tee)) formatter.output_fd = filemock - formatter.write(line, source) + + with mock.patch('flake8.formatting.base.print') as print_func: + formatter.write(line, source) + if tee: + assert print_func.called + assert print_func.mock_calls == [ + mock.call(line), + mock.call(source), + ] + else: + assert not print_func.called assert filemock.write.called is True assert filemock.write.call_count == 2