From c163370212e44e010015575dd4b81b153394f14a Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 1 Mar 2022 11:21:16 +0100 Subject: [PATCH] [#455] Raising an exception if bad mypy options are found --- CHANGELOG.rst | 1 + MANIFEST.in | 4 --- prospector/tools/exceptions.py | 3 +++ prospector/tools/mypy/__init__.py | 25 ++++++++++++++++--- tests/tools/mypy/test_mypy_tool.py | 21 ++++++++++++++++ .../mypy/test_profiles/mypy_bad_options.yaml | 5 ++++ .../mypy/test_profiles/mypy_good_options.yaml | 6 +++++ 7 files changed, 58 insertions(+), 7 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 prospector/tools/exceptions.py create mode 100644 tests/tools/mypy/test_profiles/mypy_bad_options.yaml create mode 100644 tests/tools/mypy/test_profiles/mypy_good_options.yaml diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 97c8ac94..c60fb181 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ Just say no to bugs. * Stopped the ProfileValidator tool raising errors about ``pep8`` and ``pep257`` sections being unknown. Instead, they raise deprecated warnings. * Blending works again - for example, pylint and pycodestyle errors representing the same thing are combined. After renaming pep8 to pycodestyle, this only worked when using legacy names. +* Unrecognised Mypy options now raise an exception instead of silently carrying on - `#455 `_ **Tidyup**: diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 9ea20d2f..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include LICENSE -include prospector/blender_combinations.yaml -include prospector/profiles/profiles/*.yaml -include README.rst diff --git a/prospector/tools/exceptions.py b/prospector/tools/exceptions.py new file mode 100644 index 00000000..a0da7604 --- /dev/null +++ b/prospector/tools/exceptions.py @@ -0,0 +1,3 @@ +class BadToolConfig(Exception): + def __init__(self, tool_name: str, message: str): + super().__init__(f"Bad option value found for tool {tool_name}: {message}") diff --git a/prospector/tools/mypy/__init__.py b/prospector/tools/mypy/__init__.py index 935d2a79..b6e19709 100644 --- a/prospector/tools/mypy/__init__.py +++ b/prospector/tools/mypy/__init__.py @@ -5,8 +5,20 @@ __all__ = ("MypyTool",) - -MYPY_OPTIONS = ["allow", "check", "disallow", "no-check", "no-warn", "warn"] +from prospector.tools.exceptions import BadToolConfig + +LIST_OPTIONS = ["allow", "check", "disallow", "no-check", "no-warn", "warn"] +VALID_OPTIONS = LIST_OPTIONS + [ + "strict", + "follow-imports", + "ignore-missing-imports", + "implicit-optional", + "strict-optional", + "platform", + "python-2-mode", + "python-version", + "namespace-packages", +] def format_message(message): @@ -47,6 +59,13 @@ def __init__(self, *args, **kwargs): def configure(self, prospector_config, _): options = prospector_config.tool_options("mypy") + for option_key in options.keys(): + if option_key not in VALID_OPTIONS: + url = "https://github.com/PyCQA/prospector/blob/master/prospector/tools/mypy/__init__.py" + raise BadToolConfig( + "mypy", f"Option {option_key} is not valid. " f"See the list of possible options: {url}" + ) + strict = options.get("strict", False) follow_imports = options.get("follow-imports", "normal") @@ -84,7 +103,7 @@ def configure(self, prospector_config, _): if namespace_packages: self.options.append("--namespace-packages") - for list_option in MYPY_OPTIONS: + for list_option in LIST_OPTIONS: for entry in options.get(list_option, []): self.options.append(f"--{list_option}-{entry}") diff --git a/tests/tools/mypy/test_mypy_tool.py b/tests/tools/mypy/test_mypy_tool.py index 90c3c5b0..01ad9d0a 100644 --- a/tests/tools/mypy/test_mypy_tool.py +++ b/tests/tools/mypy/test_mypy_tool.py @@ -1,6 +1,11 @@ +from pathlib import Path from unittest import SkipTest, TestCase +from unittest.mock import patch +from prospector.config import ProspectorConfig +from prospector.finder import find_python from prospector.message import Location, Message +from prospector.tools.exceptions import BadToolConfig try: from prospector.tools.mypy import format_message @@ -9,6 +14,22 @@ class TestMypyTool(TestCase): + @staticmethod + def _get_config(profile_name: str) -> ProspectorConfig: + profile_path = Path(__file__).parent / f"test_profiles/{profile_name}.yaml" + with patch("sys.argv", ["prospector", "--profile", str(profile_path.absolute())]): + return ProspectorConfig() + + def test_unrecognised_options(self): + finder = find_python([], [], True, Path(__file__).parent.absolute()) + self.assertRaises(BadToolConfig, self._get_config("mypy_bad_options").get_tools, finder) + + def test_good_options(self): + finder = find_python([], [], True, Path(__file__).parent.absolute()) + self._get_config("mypy_good_options").get_tools(finder) + + +class TestMypyMessageFormat(TestCase): def test_format_message_with_character(self): location = Location(path="file.py", module=None, function=None, line=17, character=2) expected = Message(source="mypy", code="error", location=location, message="Important error") diff --git a/tests/tools/mypy/test_profiles/mypy_bad_options.yaml b/tests/tools/mypy/test_profiles/mypy_bad_options.yaml new file mode 100644 index 00000000..2367d71a --- /dev/null +++ b/tests/tools/mypy/test_profiles/mypy_bad_options.yaml @@ -0,0 +1,5 @@ + +mypy: + run: yes + options: + warn-unreachable: true diff --git a/tests/tools/mypy/test_profiles/mypy_good_options.yaml b/tests/tools/mypy/test_profiles/mypy_good_options.yaml new file mode 100644 index 00000000..dd73107e --- /dev/null +++ b/tests/tools/mypy/test_profiles/mypy_good_options.yaml @@ -0,0 +1,6 @@ + +mypy: + run: yes + options: + warn: + - unreachable