From c4a770dd8b91a0d16ba62552cdbdd6638e252bba Mon Sep 17 00:00:00 2001 From: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> Date: Fri, 2 Sep 2022 09:35:16 +0200 Subject: [PATCH] Fix false positive for ``too-many-function-args`` when a function call is assigned to a class attribute inside the class where the function is defined. (#7395) Closes #6592 --- doc/whatsnew/fragments/6592.false_positive | 3 +++ pylint/checkers/typecheck.py | 13 +++++++++++++ tests/functional/a/arguments.py | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 doc/whatsnew/fragments/6592.false_positive diff --git a/doc/whatsnew/fragments/6592.false_positive b/doc/whatsnew/fragments/6592.false_positive new file mode 100644 index 0000000000..846ddce961 --- /dev/null +++ b/doc/whatsnew/fragments/6592.false_positive @@ -0,0 +1,3 @@ +Fix false positive for ``too-many-function-args`` when a function call is assigned to a class attribute inside the class where the function is defined. + +Closes #6592 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 9732ede783..f2518beae6 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -1458,6 +1458,19 @@ def visit_call(self, node: nodes.Call) -> None: keyword_args += list(already_filled_keywords) num_positional_args += implicit_args + already_filled_positionals + # Decrement `num_positional_args` by 1 when a function call is assigned to a class attribute + # inside the class where the function is defined. + # This avoids emitting `too-many-function-args` since `num_positional_args` + # includes an implicit `self` argument which is not present in `called.args`. + if ( + isinstance(node.frame(), nodes.ClassDef) + and isinstance(node.parent, (nodes.Assign, nodes.AnnAssign)) + and isinstance(called, nodes.FunctionDef) + and called in node.frame().body + and num_positional_args > 0 + ): + num_positional_args -= 1 + # Analyze the list of formal parameters. args = list(itertools.chain(called.args.posonlyargs or (), called.args.args)) num_mandatory_parameters = len(args) - len(called.args.defaults) diff --git a/tests/functional/a/arguments.py b/tests/functional/a/arguments.py index a065e517e0..6929b98500 100644 --- a/tests/functional/a/arguments.py +++ b/tests/functional/a/arguments.py @@ -1,6 +1,6 @@ # pylint: disable=too-few-public-methods, missing-docstring,import-error,wrong-import-position # pylint: disable=wrong-import-order, unnecessary-lambda, consider-using-f-string -# pylint: disable=unnecessary-lambda-assignment +# pylint: disable=unnecessary-lambda-assignment, no-self-argument, unused-argument def decorator(fun): """Decorator""" @@ -261,3 +261,20 @@ def func(one, two, three): CALL = lambda *args: func(*args) + + +# Ensure `too-many-function-args` is not emitted when a function call is assigned +# to a class attribute inside the class where the function is defined. +# Reference: https://github.com/PyCQA/pylint/issues/6592 +class FruitPicker: + def _pick_fruit(fruit): + def _print_selection(self): + print(f"Selected: {fruit}!") + return _print_selection + + pick_apple = _pick_fruit("apple") + pick_pear = _pick_fruit("pear") + +picker = FruitPicker() +picker.pick_apple() +picker.pick_pear()