From 7f4b21fe6fad4994a2aaa76bd1f9a955ec73a59a Mon Sep 17 00:00:00 2001 From: rmorshea Date: Thu, 23 Feb 2023 01:25:53 -0800 Subject: [PATCH] Declare function mutators with inline comment This is accomplised by defining an "error" called decorator-preserves-signature which, when disabled via a comment on a decorator definition, will disable function argument checks on calls to functions that have that decorator. We use an error to do this in order to consume the inline comment state via the linter.file_state._supression_mapping --- .gitignore | 1 + pylint/checkers/typecheck.py | 21 +++++++++++++++++---- tests/functional/a/arguments.py | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d807609565..e8ec590f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ build-stamp .pytest_cache/ .mypy_cache/ .benchmarks/ +venv diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 8448a0f13b..8a5c051db0 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -381,6 +381,11 @@ def _missing_member_hint( "Used when a slice step is 0 and the object doesn't implement " "a custom __getitem__ method.", ), + "E1145": ( + "", + "decorator-preserves-signature", + "Ignore invalid argument errors on calls to this function", + ), "W1113": ( "Keyword argument before variable positional arguments list " "in the definition of %s function", @@ -1454,10 +1459,18 @@ def visit_call(self, node: nodes.Call) -> None: return # Has the function signature changed in ways we cannot reliably detect? - if hasattr(called, "decorators") and decorated_with( - called, self.linter.config.signature_mutators - ): - return + if getattr(called, "decorators", None): + if decorated_with(called, self.linter.config.signature_mutators): + return + + called_decorator: astroid.NodeNG + for called_decorator in filter( + None, map(safe_infer, called.decorators.nodes) + ): + if not self.linter.file_state._module_msgs_state.get("E1145", {}).get( + called_decorator.lineno, True + ): + return num_positional_args = len(call_site.positional_arguments) keyword_args = list(call_site.keyword_arguments.keys()) diff --git a/tests/functional/a/arguments.py b/tests/functional/a/arguments.py index 6929b98500..bf77dcbc69 100644 --- a/tests/functional/a/arguments.py +++ b/tests/functional/a/arguments.py @@ -240,6 +240,17 @@ def wrapper(*args, do_something=True, **kwargs): return wrapper +def yet_another_mutation_decorator(fun): # pylint: disable=decorator-preserves-signature + """Yet another decorator that changes a function's signature""" + def wrapper(*args, do_something=True, **kwargs): + if do_something: + return fun(*args, **kwargs) + + return None + + return wrapper + + @mutation_decorator def mutated_function(arg): return arg @@ -250,11 +261,17 @@ def mutated(arg): return arg +@yet_another_mutation_decorator +def another_mutated_function(arg): + return arg + + mutated_function(do_something=False) mutated_function() mutated(do_something=True) +another_mutated_function(do_something=False) def func(one, two, three): return one + two + three