diff --git a/src/flake8/plugins/finder.py b/src/flake8/plugins/finder.py index 3731db9..3c576e9 100644 --- a/src/flake8/plugins/finder.py +++ b/src/flake8/plugins/finder.py @@ -1,7 +1,9 @@ """Functions related to finding and loading plugins.""" import configparser import inspect +import itertools import logging +import re import sys from typing import Any from typing import Dict @@ -20,6 +22,8 @@ from flake8.exceptions import FailedToLoadPlugin LOG = logging.getLogger(__name__) +VALID_CODE = re.compile("^[A-Z]{1,3}[0-9]{0,3}$", re.ASCII) + FLAKE8_GROUPS = frozenset(("flake8.extension", "flake8.report")) BANNED_PLUGINS = { @@ -328,6 +332,13 @@ def _classify_plugins( else: raise NotImplementedError(f"what plugin type? {loaded}") + for loaded in itertools.chain(tree, logical_line, physical_line): + if not VALID_CODE.match(loaded.entry_name): + raise ExecutionError( + f"plugin code for `{loaded.display_name}` does not match " + f"{VALID_CODE.pattern}" + ) + return Plugins( checkers=Checkers( tree=tree, diff --git a/tests/unit/plugins/finder_test.py b/tests/unit/plugins/finder_test.py index e1ab858..91cb11f 100644 --- a/tests/unit/plugins/finder_test.py +++ b/tests/unit/plugins/finder_test.py @@ -29,6 +29,37 @@ def _loaded(plugin=None, obj=None, parameters=None): return finder.LoadedPlugin(plugin, obj, parameters) +@pytest.mark.parametrize( + "s", + ( + "E", + "E1", + "E123", + "ABC", + "ABC1", + "ABC123", + ), +) +def test_valid_plugin_prefixes(s): + assert finder.VALID_CODE.match(s) + + +@pytest.mark.parametrize( + "s", + ( + "", + "A1234", + "ABCD", + "abc", + "a-b", + "☃", + "A𝟗", + ), +) +def test_invalid_plugin_prefixes(s): + assert finder.VALID_CODE.match(s) is None + + def test_loaded_plugin_entry_name_vs_display_name(): loaded = _loaded(_plugin(package="package-name", ep=_ep(name="Q"))) assert loaded.entry_name == "Q" @@ -761,6 +792,35 @@ def test_classify_plugins_enable_a_disabled_plugin(): ) +def test_classify_plugins_does_not_error_on_reporter_prefix(): + # these are ok, don't check their name + plugin = _plugin(ep=_ep(name="report-er", group="flake8.report")) + loaded = _loaded(plugin=plugin) + + opts = finder.PluginOptions.blank() + classified = finder._classify_plugins([loaded], opts) + + assert classified == finder.Plugins( + checkers=finder.Checkers([], [], []), + reporters={"report-er": loaded}, + disabled=[], + ) + + +def test_classify_plugins_errors_on_incorrect_checker_name(): + plugin = _plugin(ep=_ep(name="INVALID", group="flake8.extension")) + loaded = _loaded(plugin=plugin, parameters={"tree": True}) + + with pytest.raises(ExecutionError) as excinfo: + finder._classify_plugins([loaded], finder.PluginOptions.blank()) + + (msg,) = excinfo.value.args + assert msg == ( + "plugin code for `local[INVALID]` " + "does not match ^[A-Z]{1,3}[0-9]{0,3}$" + ) + + @pytest.mark.usefixtures("reset_sys") def test_load_plugins(): plugin = _plugin(ep=_ep(value="aplugin:ExtensionTestPlugin2"))