From 9ba6677c4ae4248b0f31d861a608db2efcc2679a Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 8 Jul 2019 20:12:47 +0000 Subject: [PATCH] support extend-exclude Fixes #535 --- docs/source/user/options.rst | 32 ++++++++++++++++++++++++++++++ src/flake8/checker.py | 6 +++++- src/flake8/main/options.py | 11 ++++++++++ tests/integration/test_checker.py | 3 +-- tests/integration/test_main.py | 16 +++++++++++++++ tests/unit/test_checker_manager.py | 11 +++++----- 6 files changed, 70 insertions(+), 9 deletions(-) diff --git a/docs/source/user/options.rst b/docs/source/user/options.rst index 3b1c43d..ce0cf18 100644 --- a/docs/source/user/options.rst +++ b/docs/source/user/options.rst @@ -257,6 +257,38 @@ Options and their Descriptions __pycache__ +.. option:: --extend-exclude= + + :ref:`Go back to index ` + + .. versionadded:: 3.8.0 + + Provide a comma-separated list of glob patterns to add to the list of excluded ones. + Similar considerations as in :option:`--exclude` apply here with regard to the + value. + + The difference to the :option:`--exclude` option is, that this option can be + used to selectively add individual patterns without overriding the default + list entirely. + + Command-line example: + + .. prompt:: bash + + flake8 --extend-exclude=legacy/,vendor/ dir/ + + This **can** be specified in config files. + + Example config file usage: + + .. code-block:: ini + + extend-exclude = + legacy/, + vendor/ + extend-exclude = legacy/,vendor/ + + .. option:: --filename= :ref:`Go back to index ` diff --git a/src/flake8/checker.py b/src/flake8/checker.py index 52a6a70..405820c 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -1,6 +1,7 @@ """Checker Manager and Checker classes.""" import collections import errno +import itertools import logging import signal import tokenize @@ -79,6 +80,9 @@ class Manager(object): "physical lines": 0, "tokens": 0, } + self.exclude = tuple( + itertools.chain(self.options.exclude, self.options.extend_exclude), + ) def _process_statistics(self): for checker in self.checkers: @@ -187,7 +191,7 @@ class Manager(object): return utils.matches_filename( path, - patterns=self.options.exclude, + patterns=self.exclude, log_message='"%(path)s" has %(whether)sbeen excluded', logger=LOG, ) diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py index d2a7159..e1f1767 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -14,6 +14,7 @@ def register_default_options(option_manager): - ``--count`` - ``--diff`` - ``--exclude`` + - ``--extend-exclude`` - ``--filename`` - ``--format`` - ``--hang-closing`` @@ -85,6 +86,16 @@ def register_default_options(option_manager): " (Default: %default)", ) + add_option( + "--extend-exclude", + metavar="patterns", + default="", + parse_from_config=True, + comma_separated_list=True, + help="Comma-separated list of files or directories to add to the list" + " of excluded ones." + ) + add_option( "--filename", metavar="patterns", diff --git a/tests/integration/test_checker.py b/tests/integration/test_checker.py index a17e5cd..ca3ed4f 100644 --- a/tests/integration/test_checker.py +++ b/tests/integration/test_checker.py @@ -206,8 +206,7 @@ def test_report_order(results, expected_order): file_checker.results = results file_checker.display_name = 'placeholder' - style_guide = mock.Mock(spec=['options']) - style_guide.processing_file = mock.MagicMock() + style_guide = mock.MagicMock(spec=['options', 'processing_file']) # Create a placeholder manager without arguments or plugins # Just add one custom file checker which just provides the results diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index 43cc168..21fd9e0 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -60,6 +60,22 @@ t.py:2:1: F401 'sys' imported but unused assert err == '' +def test_extend_exclude(tmpdir, capsys): + """Ensure that `flake8 --extend-exclude` works.""" + for d in ['project', 'vendor', 'legacy', '.git', '.tox', '.hg']: + tmpdir.mkdir(d).join('t.py').write('import os\nimport sys\n') + + with tmpdir.as_cwd(): + application.Application().run(['--extend-exclude=vendor,legacy']) + + out, err = capsys.readouterr() + assert out == '''\ +./project/t.py:1:1: F401 'os' imported but unused +./project/t.py:2:1: F401 'sys' imported but unused +''' + assert err == '' + + def test_malformed_per_file_ignores_error(tmpdir, capsys): """Test the error message for malformed `per-file-ignores`.""" setup_cfg = '''\ diff --git a/tests/unit/test_checker_manager.py b/tests/unit/test_checker_manager.py index 50f4084..2a6998e 100644 --- a/tests/unit/test_checker_manager.py +++ b/tests/unit/test_checker_manager.py @@ -7,13 +7,12 @@ import pytest from flake8 import checker -def style_guide_mock(**kwargs): +def style_guide_mock(): """Create a mock StyleGuide object.""" - kwargs.setdefault('diff', False) - kwargs.setdefault('jobs', '4') - style_guide = mock.Mock() - style_guide.options = mock.Mock(**kwargs) - return style_guide + return mock.MagicMock(**{ + 'options.diff': False, + 'options.jobs': '4', + }) def _parallel_checker_manager():