diff --git a/changelog/452.fix.md b/changelog/452.fix.md new file mode 100644 index 00000000..87739bd6 --- /dev/null +++ b/changelog/452.fix.md @@ -0,0 +1 @@ +disallow both class args with flake8 \ No newline at end of file diff --git a/docs/usage/config-messages.rst b/docs/usage/config-messages.rst index a0faffe5..fccd0a26 100644 --- a/docs/usage/config-messages.rst +++ b/docs/usage/config-messages.rst @@ -17,3 +17,8 @@ :titlesonly: messages/sig004-unknown-inline-directive-option + +.. toctree:: + :titlesonly: + + messages/sig005-mutually-exclusive-options diff --git a/docs/usage/messages/sig005-mutually-exclusive-options.rst b/docs/usage/messages/sig005-mutually-exclusive-options.rst new file mode 100644 index 00000000..5a7a0063 --- /dev/null +++ b/docs/usage/messages/sig005-mutually-exclusive-options.rst @@ -0,0 +1,19 @@ +SIG005: mutually-exclusive-options +================================== + +Both mutually exclusive class options configured + +This is only raised as a ``flake8`` violation so that it won't cause ``flake8`` to +totally crash and other plugins can continue to run + +When running ``docsig`` on it's own an exception will be raised instead + +.. code-block:: console + + $ flake8 example.py --sig-check-class --sig-check-class-constructor + example.py:0:1: SIG005 both class and class constructor passed to commandline (mutually-exclusive-class-args) + +.. code-block:: console + + $ docsig example.py --check-class --check-class-constructor 2>&1 | tail -n 1 + docsig: error: argument -C/--check-class-constructor: not allowed with argument -c/--check-class diff --git a/docsig/messages.py b/docsig/messages.py index 96292d69..5f46cf7f 100644 --- a/docsig/messages.py +++ b/docsig/messages.py @@ -130,6 +130,12 @@ def all(self) -> Messages: "unknown inline comment option for {directive} '{option}'", "unknown-inline-directive-option", ), + 5: Message( + "SIG005", + "SIG005", + "both mutually exclusive class options configured", + "mutually-exclusive-options", + ), #: SIG1xx Missing 101: Message( "SIG101", diff --git a/docsig/plugin.py b/docsig/plugin.py index 72df3e6c..4fb27ecb 100644 --- a/docsig/plugin.py +++ b/docsig/plugin.py @@ -8,7 +8,7 @@ from ._config import merge_configs as _merge_configs from ._core import runner from ._version import __version__ -from .messages import FLAKE8 +from .messages import FLAKE8, E Flake8Error = t.Tuple[int, int, str, t.Type] @@ -135,32 +135,42 @@ def run(self) -> t.Generator[Flake8Error, None, None]: :return: Flake8 error, if there is one. """ - results = runner( - self.filename, - check_class=self.a.check_class, - check_class_constructor=self.a.check_class_constructor, - check_dunders=self.a.check_dunders, - check_protected_class_methods=( - self.a.check_protected_class_methods - ), - check_nested=self.a.check_nested, - check_overridden=self.a.check_overridden, - check_protected=self.a.check_protected, - check_property_returns=self.a.check_property_returns, - ignore_no_params=self.a.ignore_no_params, - ignore_args=self.a.ignore_args, - ignore_kwargs=self.a.ignore_kwargs, - ignore_typechecker=self.a.ignore_typechecker, - verbose=self.a.verbose, - )[0] - for result in results: - for info in result: - line = "{msg} '{name}'".format( - msg=FLAKE8.format( - ref=info.ref, - description=info.description, - symbolic=info.symbolic, - ), - name=info.name, - ) - yield info.lineno, 0, line, self.__class__ + if self.a.check_class and self.a.check_class_constructor: + line = "{msg}".format( + msg=FLAKE8.format( + ref=E[5].ref, + description=E[5].description, + symbolic=E[5].symbolic, + ), + ) + yield 0, 0, line, self.__class__ + else: + results = runner( + self.filename, + check_class=self.a.check_class, + check_class_constructor=self.a.check_class_constructor, + check_dunders=self.a.check_dunders, + check_protected_class_methods=( + self.a.check_protected_class_methods + ), + check_nested=self.a.check_nested, + check_overridden=self.a.check_overridden, + check_protected=self.a.check_protected, + check_property_returns=self.a.check_property_returns, + ignore_no_params=self.a.ignore_no_params, + ignore_args=self.a.ignore_args, + ignore_kwargs=self.a.ignore_kwargs, + ignore_typechecker=self.a.ignore_typechecker, + verbose=self.a.verbose, + )[0] + for result in results: + for info in result: + line = "{msg} '{name}'".format( + msg=FLAKE8.format( + ref=info.ref, + description=info.description, + symbolic=info.symbolic, + ), + name=info.name, + ) + yield info.lineno, 0, line, self.__class__ diff --git a/tests/fix_test.py b/tests/fix_test.py index 6ce25419..f3fe2d8a 100644 --- a/tests/fix_test.py +++ b/tests/fix_test.py @@ -11,8 +11,16 @@ import pytest import docsig - -from . import TREE, FixtureMakeTree, InitFileFixtureType, MockMainType, long +import docsig.plugin + +from . import ( + TREE, + FixtureFlake8, + FixtureMakeTree, + InitFileFixtureType, + MockMainType, + long, +) def test_fix_optional_return_statements_with_overload_func_sig502( @@ -363,3 +371,29 @@ def test_indent_427( main(".", test_flake8=False) std = capsys.readouterr() assert "SIG401" in std.out + + +def test_class_and_class_constructor_452( + capsys: pytest.CaptureFixture, + flake8: FixtureFlake8, + init_file: InitFileFixtureType, +) -> None: + """Test command lines errors when passed incompatible options. + + :param capsys: Capture sys out. + :param flake8: Patch package entry point. + :param init_file: Initialize a test file. + """ + template = ''' +def function(param1, param2, param3) -> None: + """Proper docstring. + + :param param1: Passes. + :param param2: Passes. + :param param3: Passes. + """ +''' + init_file(template) + flake8(".", "--sig-check-class", "--sig-check-class-constructor") + std = capsys.readouterr() + assert docsig.messages.E[5].description in std.out