summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Sottile <asottile@umich.edu>2022-01-01 18:28:11 -0500
committerAnthony Sottile <asottile@umich.edu>2022-01-01 18:33:07 -0500
commita8333e2bf22dbbf22739a554a70e094d2c80f460 (patch)
tree5b39d29f1636de35ebf46a84cb1911db2b094cbe
parentff13916d74098adda648f02998c838317c0352e5 (diff)
downloadflake8-off_by_default.tar.gz
move managing of off_by_default / enable_extensions to plugin loadingoff_by_default
-rw-r--r--src/flake8/api/legacy.py2
-rw-r--r--src/flake8/main/application.py8
-rw-r--r--src/flake8/main/options.py18
-rw-r--r--src/flake8/options/manager.py6
-rw-r--r--src/flake8/plugins/finder.py43
-rw-r--r--src/flake8/style_guide.py7
-rw-r--r--tests/integration/test_checker.py2
-rw-r--r--tests/integration/test_plugins.py5
-rw-r--r--tests/unit/plugins/finder_test.py53
-rw-r--r--tests/unit/test_debug.py1
-rw-r--r--tests/unit/test_decision_engine.py100
-rw-r--r--tests/unit/test_legacy_api.py3
12 files changed, 137 insertions, 111 deletions
diff --git a/src/flake8/api/legacy.py b/src/flake8/api/legacy.py
index 0d9875f..0d848fc 100644
--- a/src/flake8/api/legacy.py
+++ b/src/flake8/api/legacy.py
@@ -38,7 +38,7 @@ def get_style_guide(**kwargs):
isolated=prelim_opts.isolated,
)
- application.find_plugins(cfg, cfg_dir)
+ application.find_plugins(cfg, cfg_dir, prelim_opts.enable_extensions)
application.register_plugin_options()
application.parse_configuration_and_cli(cfg, cfg_dir, remaining_args)
# We basically want application.initialize to be called but with these
diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py
index 86fd89f..4b91145 100644
--- a/src/flake8/main/application.py
+++ b/src/flake8/main/application.py
@@ -119,14 +119,16 @@ class Application:
self,
cfg: configparser.RawConfigParser,
cfg_dir: str,
+ enable_extensions: Optional[str],
) -> None:
"""Find and load the plugins for this application.
Set :attr:`plugins` based on loaded plugins.
"""
- raw_plugins = finder.find_plugins(cfg)
+ raw = finder.find_plugins(cfg)
local_plugin_paths = finder.find_local_plugin_paths(cfg, cfg_dir)
- self.plugins = finder.load_plugins(raw_plugins, local_plugin_paths)
+ enabled = finder.parse_enabled(cfg, enable_extensions)
+ self.plugins = finder.load_plugins(raw, local_plugin_paths, enabled)
def register_plugin_options(self) -> None:
"""Register options provided by plugins to our option manager."""
@@ -302,7 +304,7 @@ class Application:
isolated=prelim_opts.isolated,
)
- self.find_plugins(cfg, cfg_dir)
+ self.find_plugins(cfg, cfg_dir, prelim_opts.enable_extensions)
self.register_plugin_options()
self.parse_configuration_and_cli(cfg, cfg_dir, remaining_args)
self.make_formatter()
diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py
index 9c23d64..93178ac 100644
--- a/src/flake8/main/options.py
+++ b/src/flake8/main/options.py
@@ -15,6 +15,7 @@ def stage1_arg_parser() -> argparse.ArgumentParser:
- ``--append-config``
- ``--config``
- ``--isolated``
+ - ``--enable-extensions``
"""
parser = argparse.ArgumentParser(add_help=False)
@@ -59,6 +60,14 @@ def stage1_arg_parser() -> argparse.ArgumentParser:
help="Ignore all configuration files.",
)
+ # Plugin enablement options
+
+ parser.add_argument(
+ "--enable-extensions",
+ help="Enable plugins and extensions that are otherwise disabled "
+ "by default",
+ )
+
return parser
@@ -116,7 +125,6 @@ def register_default_options(option_manager: OptionManager) -> None:
- ``--disable-noqa``
- ``--show-source``
- ``--statistics``
- - ``--enable-extensions``
- ``--exit-zero``
- ``-j``/``--jobs``
- ``--tee``
@@ -331,14 +339,6 @@ def register_default_options(option_manager: OptionManager) -> None:
)
# Flake8 options
- add_option(
- "--enable-extensions",
- default="",
- parse_from_config=True,
- comma_separated_list=True,
- help="Enable plugins and extensions that are otherwise disabled "
- "by default",
- )
add_option(
"--exit-zero",
diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py
index d448e1b..d3f6ec7 100644
--- a/src/flake8/options/manager.py
+++ b/src/flake8/options/manager.py
@@ -376,11 +376,7 @@ class OptionManager:
_set_group(loaded.plugin.package)
add_options(self)
- # if the plugin is off by default, disable it!
- if getattr(loaded.obj, "off_by_default", False):
- self.extend_default_ignore(loaded.entry_name)
- else:
- self.extend_default_select(loaded.entry_name)
+ self.extend_default_select(loaded.entry_name)
# isn't strictly necessary, but seems cleaner
self._current_group = None
diff --git a/src/flake8/plugins/finder.py b/src/flake8/plugins/finder.py
index 4609474..fcd6aff 100644
--- a/src/flake8/plugins/finder.py
+++ b/src/flake8/plugins/finder.py
@@ -9,6 +9,8 @@ from typing import Generator
from typing import Iterable
from typing import List
from typing import NamedTuple
+from typing import Optional
+from typing import Set
from flake8 import utils
from flake8._compat import importlib_metadata
@@ -63,6 +65,7 @@ class Plugins(NamedTuple):
checkers: Checkers
reporters: Dict[str, LoadedPlugin]
+ disabled: List[LoadedPlugin]
def all_plugins(self) -> Generator[LoadedPlugin, None, None]:
"""Return an iterator over all :class:`LoadedPlugin`s."""
@@ -171,6 +174,24 @@ def find_local_plugin_paths(
return utils.normalize_paths(paths, cfg_dir)
+def parse_enabled(
+ cfg: configparser.RawConfigParser,
+ enable_extensions: Optional[str],
+) -> Set[str]:
+ """Parse --enable-extensions."""
+ if enable_extensions is not None:
+ return set(utils.parse_comma_separated_list(enable_extensions))
+ else:
+ # ideally this would reuse our config parsing framework but we need to
+ # parse this from preliminary options before plugins are enabled
+ for opt in ("enable_extensions", "enable-extensions"):
+ val = cfg.get("flake8", opt, fallback=None)
+ if val is not None:
+ return set(utils.parse_comma_separated_list(val))
+ else:
+ return set()
+
+
def _parameters_for(func: Any) -> Dict[str, bool]:
"""Return the parameters for the plugin.
@@ -218,14 +239,23 @@ def _import_plugins(
return [_load_plugin(p) for p in plugins]
-def _classify_plugins(plugins: List[LoadedPlugin]) -> Plugins:
+def _classify_plugins(
+ plugins: List[LoadedPlugin],
+ enabled: Set[str],
+) -> Plugins:
tree = []
logical_line = []
physical_line = []
reporters = {}
+ disabled = []
for loaded in plugins:
- if loaded.plugin.entry_point.group == "flake8.report":
+ if (
+ getattr(loaded.obj, "off_by_default", False)
+ and loaded.plugin.entry_point.name not in enabled
+ ):
+ disabled.append(loaded)
+ elif loaded.plugin.entry_point.group == "flake8.report":
reporters[loaded.entry_name] = loaded
elif "tree" in loaded.parameters:
tree.append(loaded)
@@ -243,14 +273,19 @@ def _classify_plugins(plugins: List[LoadedPlugin]) -> Plugins:
physical_line=physical_line,
),
reporters=reporters,
+ disabled=disabled,
)
-def load_plugins(plugins: List[Plugin], paths: List[str]) -> Plugins:
+def load_plugins(
+ plugins: List[Plugin],
+ paths: List[str],
+ enabled: Set[str],
+) -> Plugins:
"""Load and classify all flake8 plugins.
- first: extends ``sys.path`` with ``paths`` (to import local plugins)
- next: converts the ``Plugin``s to ``LoadedPlugins``
- finally: classifies plugins into their specific types
"""
- return _classify_plugins(_import_plugins(plugins, paths))
+ return _classify_plugins(_import_plugins(plugins, paths), enabled)
diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py
index aca743a..c471fa6 100644
--- a/src/flake8/style_guide.py
+++ b/src/flake8/style_guide.py
@@ -160,14 +160,9 @@ class DecisionEngine:
self.extended_selected = tuple(
sorted(options.extended_default_select, reverse=True)
)
- self.enabled_extensions = tuple(options.enable_extensions)
self.all_selected = tuple(
sorted(
- itertools.chain(
- self.selected,
- options.extend_select,
- self.enabled_extensions,
- ),
+ itertools.chain(self.selected, options.extend_select),
reverse=True,
)
)
diff --git a/tests/integration/test_checker.py b/tests/integration/test_checker.py
index 96b7d4b..9583179 100644
--- a/tests/integration/test_checker.py
+++ b/tests/integration/test_checker.py
@@ -90,7 +90,7 @@ def mock_file_checker_with_plugin(plugin_target):
),
),
]
- plugins = finder.load_plugins(to_load, [])
+ plugins = finder.load_plugins(to_load, [], set())
# Prevent it from reading lines from stdin or somewhere else
with mock.patch(
diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py
index 682360c..e2c87e4 100644
--- a/tests/integration/test_plugins.py
+++ b/tests/integration/test_plugins.py
@@ -55,7 +55,7 @@ def test_enable_local_plugin_from_config(local_config):
cfg, cfg_dir = config.load_config(local_config, [], isolated=False)
plugins = finder.find_plugins(cfg)
plugin_paths = finder.find_local_plugin_paths(cfg, cfg_dir)
- loaded_plugins = finder.load_plugins(plugins, plugin_paths)
+ loaded_plugins = finder.load_plugins(plugins, plugin_paths, set())
(custom_extension,) = (
loaded
@@ -82,7 +82,8 @@ def test_local_plugin_can_add_option(local_config):
plugins = finder.find_plugins(cfg)
plugin_paths = finder.find_local_plugin_paths(cfg, cfg_dir)
- loaded_plugins = finder.load_plugins(plugins, plugin_paths)
+ enabled = finder.parse_enabled(cfg, stage1_args.enable_extensions)
+ loaded_plugins = finder.load_plugins(plugins, plugin_paths, enabled)
option_manager = OptionManager(
version="123",
diff --git a/tests/unit/plugins/finder_test.py b/tests/unit/plugins/finder_test.py
index 993c13c..f41aba4 100644
--- a/tests/unit/plugins/finder_test.py
+++ b/tests/unit/plugins/finder_test.py
@@ -49,6 +49,7 @@ def test_plugins_all_plugins():
physical_line=[physical_line_plugin],
),
reporters={"R": report_plugin},
+ disabled=[],
)
assert tuple(plugins.all_plugins()) == (
@@ -72,6 +73,7 @@ def test_plugins_versions_str():
# ignore local plugins
"custom": _loaded(_plugin(package="local")),
},
+ disabled=[],
)
assert plugins.versions_str() == "pkg1: 1, pkg2: 2"
@@ -374,6 +376,25 @@ def test_find_local_plugins(local_plugin_cfg):
}
+def test_parse_enabled_not_specified():
+ assert finder.parse_enabled(configparser.RawConfigParser(), None) == set()
+
+
+def test_parse_enabled_from_commandline():
+ cfg = configparser.RawConfigParser()
+ cfg.add_section("flake8")
+ cfg.set("flake8", "enable_extensions", "A,B,C")
+ assert finder.parse_enabled(cfg, "D,E,F") == {"D", "E", "F"}
+
+
+@pytest.mark.parametrize("opt", ("enable_extensions", "enable-extensions"))
+def test_parse_enabled_from_config(opt):
+ cfg = configparser.RawConfigParser()
+ cfg.add_section("flake8")
+ cfg.set("flake8", opt, "A,B,C")
+ assert finder.parse_enabled(cfg, None) == {"A", "B", "C"}
+
+
def test_find_plugins(
tmp_path,
flake8_dist,
@@ -573,7 +594,13 @@ def test_classify_plugins():
physical_line_plugin = _loaded(parameters={"physical_line": True})
classified = finder._classify_plugins(
- [report_plugin, tree_plugin, logical_line_plugin, physical_line_plugin]
+ [
+ report_plugin,
+ tree_plugin,
+ logical_line_plugin,
+ physical_line_plugin,
+ ],
+ set(),
)
assert classified == finder.Plugins(
@@ -583,6 +610,27 @@ def test_classify_plugins():
physical_line=[physical_line_plugin],
),
reporters={"R": report_plugin},
+ disabled=[],
+ )
+
+
+def test_classify_plugins_enable_a_disabled_plugin():
+ obj = mock.Mock(off_by_default=True)
+ plugin = _plugin(ep=_ep(name="ABC"))
+ loaded = _loaded(plugin=plugin, parameters={"tree": True}, obj=obj)
+
+ classified_normal = finder._classify_plugins([loaded], set())
+ classified_enabled = finder._classify_plugins([loaded], {"ABC"})
+
+ assert classified_normal == finder.Plugins(
+ checkers=finder.Checkers([], [], []),
+ reporters={},
+ disabled=[loaded],
+ )
+ assert classified_enabled == finder.Plugins(
+ checkers=finder.Checkers([loaded], [], []),
+ reporters={},
+ disabled=[],
)
@@ -590,7 +638,7 @@ def test_classify_plugins():
def test_load_plugins():
plugin = _plugin(ep=_ep(value="aplugin:ExtensionTestPlugin2"))
- ret = finder.load_plugins([plugin], ["tests/integration/subdir"])
+ ret = finder.load_plugins([plugin], ["tests/integration/subdir"], set())
import aplugin
@@ -607,4 +655,5 @@ def test_load_plugins():
physical_line=[],
),
reporters={},
+ disabled=[],
)
diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py
index 284dec6..1fc93ef 100644
--- a/tests/unit/test_debug.py
+++ b/tests/unit/test_debug.py
@@ -30,6 +30,7 @@ def test_debug_information():
physical_line=[],
),
reporters={},
+ disabled=[],
)
info = debug.information("9001", plugins)
diff --git a/tests/unit/test_decision_engine.py b/tests/unit/test_decision_engine.py
index 213dd84..19d574a 100644
--- a/tests/unit/test_decision_engine.py
+++ b/tests/unit/test_decision_engine.py
@@ -16,7 +16,6 @@ def create_options(**kwargs):
kwargs.setdefault("ignore", [])
kwargs.setdefault("extend_ignore", [])
kwargs.setdefault("disable_noqa", False)
- kwargs.setdefault("enable_extensions", [])
return argparse.Namespace(**kwargs)
@@ -64,30 +63,25 @@ def test_was_ignored_implicitly_selects_errors(
@pytest.mark.parametrize(
- "select_list,extend_select,enable_extensions,error_code",
- [
- (["E111", "E121"], [], [], "E111"),
- (["E111", "E121"], [], [], "E121"),
- (["E11", "E12"], [], [], "E121"),
- (["E2", "E12"], [], [], "E121"),
- (["E2", "E12"], [], [], "E211"),
- (["E1"], ["E2"], [], "E211"),
- (["E1"], [], ["E2"], "E211"),
- ([], ["E2"], [], "E211"),
- ([], [], ["E2"], "E211"),
- (["E1"], ["E2"], [], "E211"),
- (["E111"], ["E121"], ["E2"], "E121"),
- ],
+ ("select_list", "extend_select", "error_code"),
+ (
+ (["E111", "E121"], [], "E111"),
+ (["E111", "E121"], [], "E121"),
+ (["E11", "E12"], [], "E121"),
+ (["E2", "E12"], [], "E121"),
+ (["E2", "E12"], [], "E211"),
+ (["E1"], ["E2"], "E211"),
+ ([], ["E2"], "E211"),
+ (["E1"], ["E2"], "E211"),
+ (["E111"], ["E121"], "E121"),
+ ),
)
-def test_was_selected_selects_errors(
- select_list, extend_select, enable_extensions, error_code
-):
+def test_was_selected_selects_errors(select_list, extend_select, error_code):
"""Verify we detect users explicitly selecting an error."""
decider = style_guide.DecisionEngine(
options=create_options(
select=select_list,
extend_select=extend_select,
- enable_extensions=enable_extensions,
),
)
@@ -199,15 +193,20 @@ def test_decision_for(
@pytest.mark.parametrize(
- "select,ignore,extended_default_ignore,extended_default_select,"
- "enabled_extensions,error_code,expected",
+ (
+ "select",
+ "ignore",
+ "extended_default_ignore",
+ "extended_default_select",
+ "error_code",
+ "expected",
+ ),
[
(
defaults.SELECT,
[],
[],
["I1"],
- [],
"I100",
style_guide.Decision.Selected,
),
@@ -216,7 +215,6 @@ def test_decision_for(
[],
[],
["I1"],
- [],
"I201",
style_guide.Decision.Ignored,
),
@@ -225,7 +223,6 @@ def test_decision_for(
["I2"],
[],
["I1"],
- [],
"I101",
style_guide.Decision.Selected,
),
@@ -234,7 +231,6 @@ def test_decision_for(
["I2"],
[],
["I1"],
- [],
"I201",
style_guide.Decision.Ignored,
),
@@ -243,7 +239,6 @@ def test_decision_for(
["I1"],
[],
["I10"],
- [],
"I101",
style_guide.Decision.Selected,
),
@@ -252,43 +247,22 @@ def test_decision_for(
["I10"],
[],
["I1"],
- [],
"I101",
style_guide.Decision.Ignored,
),
(
defaults.SELECT,
- [],
- [],
- [],
- ["U4"],
- "U401",
- style_guide.Decision.Selected,
- ),
- (
- defaults.SELECT,
["U401"],
[],
[],
- ["U4"],
"U401",
style_guide.Decision.Ignored,
),
(
- defaults.SELECT,
- ["U401"],
- [],
- [],
- ["U4"],
- "U402",
- style_guide.Decision.Selected,
- ),
- (
["E", "W"],
["E13"],
[],
[],
- [],
"E131",
style_guide.Decision.Ignored,
),
@@ -297,18 +271,16 @@ def test_decision_for(
["E13"],
[],
[],
- [],
"E126",
style_guide.Decision.Selected,
),
- (["E2"], ["E21"], [], [], [], "E221", style_guide.Decision.Selected),
- (["E2"], ["E21"], [], [], [], "E212", style_guide.Decision.Ignored),
+ (["E2"], ["E21"], [], [], "E221", style_guide.Decision.Selected),
+ (["E2"], ["E21"], [], [], "E212", style_guide.Decision.Ignored),
(
["F", "W"],
["C90"],
[],
["I1"],
- [],
"C901",
style_guide.Decision.Ignored,
),
@@ -317,7 +289,6 @@ def test_decision_for(
["C"],
[],
[],
- [],
"E131",
style_guide.Decision.Selected,
),
@@ -325,17 +296,7 @@ def test_decision_for(
defaults.SELECT,
defaults.IGNORE,
[],
- [],
- ["I"],
- "I101",
- style_guide.Decision.Selected,
- ),
- (
- defaults.SELECT,
- defaults.IGNORE,
- [],
["G"],
- ["I"],
"G101",
style_guide.Decision.Selected,
),
@@ -344,25 +305,14 @@ def test_decision_for(
["G1"],
[],
["G"],
- ["I"],
"G101",
style_guide.Decision.Ignored,
),
(
- defaults.SELECT,
- ["E126"],
- [],
- [],
- ["I"],
- "I101",
- style_guide.Decision.Selected,
- ),
- (
["E", "W"],
defaults.IGNORE,
[],
["I"],
- [],
"I101",
style_guide.Decision.Ignored,
),
@@ -371,7 +321,6 @@ def test_decision_for(
defaults.IGNORE + ("I101",),
["I101"],
[],
- [],
"I101",
style_guide.Decision.Selected,
),
@@ -380,7 +329,6 @@ def test_decision_for(
defaults.IGNORE + ("I101",),
["I101"],
[],
- [],
"I101",
style_guide.Decision.Ignored,
),
@@ -393,7 +341,6 @@ def test_more_specific_decision_for_logic(
ignore,
extended_default_ignore,
extended_default_select,
- enabled_extensions,
error_code,
expected,
):
@@ -404,7 +351,6 @@ def test_more_specific_decision_for_logic(
ignore=ignore,
extended_default_select=extended_default_select,
extended_default_ignore=extended_default_ignore,
- enable_extensions=enabled_extensions,
),
)
diff --git a/tests/unit/test_legacy_api.py b/tests/unit/test_legacy_api.py
index 1033f5e..6424f60 100644
--- a/tests/unit/test_legacy_api.py
+++ b/tests/unit/test_legacy_api.py
@@ -19,6 +19,7 @@ def test_get_style_guide():
isolated=False,
output_file=None,
verbose=0,
+ enable_extensions=None,
)
mockedapp = mock.Mock()
mockedapp.parse_preliminary_options.return_value = (prelim_opts, [])
@@ -34,7 +35,7 @@ def test_get_style_guide():
application.assert_called_once_with()
mockedapp.parse_preliminary_options.assert_called_once_with([])
- mockedapp.find_plugins.assert_called_once_with(cfg, cfg_dir)
+ mockedapp.find_plugins.assert_called_once_with(cfg, cfg_dir, None)
mockedapp.register_plugin_options.assert_called_once_with()
mockedapp.parse_configuration_and_cli.assert_called_once_with(
cfg, cfg_dir, []