From 4d984e0a7a2fa639a95aae7441723e27b93c4590 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 | 22 ++++++++++++++++++---- tests/functional/a/arguments.py | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d8076095652..e8ec590f5b3 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 8448a0f13b2..af109cf5674 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,19 @@ 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 + + for called_decorator in filter( + None, map(safe_infer, called.decorators.nodes) + ): + if decorated_with(called, ["yet_another_mutation_decorator"]): + print(self.linter.file_state._module_msgs_state.get("E1145", {})) + 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 6929b985000..bf77dcbc692 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