From 2a69387352bd1c941759faaa22458de3609b7627 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Tue, 21 Dec 2021 08:58:15 -0500 Subject: Fix #5557: Don't emit `comparison-with-callable` if the callable raises (#5563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #5557: Don't emit `comparison-with-callable` if the callable raises Typing constants such as `typing.Any` raise when called. Co-authored-by: Pierre Sassoulas Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> --- ChangeLog | 5 +++++ doc/whatsnew/2.13.rst | 5 +++++ pylint/checkers/base.py | 20 ++++++++++++-------- tests/functional/c/comparison_with_callable.py | 11 +++++++++++ .../c/comparison_with_callable_typing_constants.py | 18 ++++++++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 tests/functional/c/comparison_with_callable_typing_constants.py diff --git a/ChangeLog b/ChangeLog index 128ae03af..f1b8c1aab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,11 @@ Release date: TBA Closes #3675 +* Fix ``comparison-with-callable`` false positive for callables that raise, such + as typing constants. + + Closes #5557 + * Fix ``unnecessary_dict_index_lookup`` false positive when deleting a dictionary's entry. Closes #4716 diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index 5e5d73493..05d4e495f 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -99,6 +99,11 @@ Other Changes * ``fatal`` was added to the variables permitted in score evaluation expressions. +* Fix ``comparison-with-callable`` false positive for callables that raise, such + as typing constants. + + Closes #5557 + * The ``PyLinter`` class will now be initialized with a ``TextReporter`` as its reporter if none is provided. diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 9d61cf10f..46eb0d17d 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -2511,14 +2511,18 @@ class ComparisonChecker(_BasicChecker): left_operand, right_operand = node.left, node.ops[0][1] # this message should be emitted only when there is comparison of bare callable # with non bare callable. - if ( - sum( - 1 - for operand in (left_operand, right_operand) - if isinstance(utils.safe_infer(operand), bare_callables) - ) - == 1 - ): + number_of_bare_callables = 0 + for operand in left_operand, right_operand: + inferred = utils.safe_infer(operand) + # Ignore callables that raise, as well as typing constants + # implemented as functions (that raise via their decorator) + if ( + isinstance(inferred, bare_callables) + and "typing._SpecialForm" not in inferred.decoratornames() + and not any(isinstance(x, nodes.Raise) for x in inferred.body) + ): + number_of_bare_callables += 1 + if number_of_bare_callables == 1: self.add_message("comparison-with-callable", node=node) @utils.check_messages( diff --git a/tests/functional/c/comparison_with_callable.py b/tests/functional/c/comparison_with_callable.py index fb02729d8..2afe43035 100644 --- a/tests/functional/c/comparison_with_callable.py +++ b/tests/functional/c/comparison_with_callable.py @@ -58,3 +58,14 @@ a = 666 b = 786 if a == b: pass + + +def eventually_raise(): + print() + raise Exception + + +if a == eventually_raise: + # Does not emit comparison-with-callable because the + # function (eventually) raises + pass diff --git a/tests/functional/c/comparison_with_callable_typing_constants.py b/tests/functional/c/comparison_with_callable_typing_constants.py new file mode 100644 index 000000000..70aa9763f --- /dev/null +++ b/tests/functional/c/comparison_with_callable_typing_constants.py @@ -0,0 +1,18 @@ +"""Typing constants are actually implemented as functions, but they +raise when called, so Pylint uses that to avoid false positives for +comparison-with-callable. +""" +from typing import Any, Optional + + +def check_any(type_) -> bool: + """See https://github.com/PyCQA/pylint/issues/5557""" + return type_ == Any + + +def check_optional(type_) -> bool: + """ + Unlike Any, Optional does not raise in its body. + It raises via its decorator: typing._SpecialForm.__call__() + """ + return type_ == Optional -- cgit v1.2.1