summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2021-12-21 08:58:15 -0500
committerGitHub <noreply@github.com>2021-12-21 14:58:15 +0100
commit2a69387352bd1c941759faaa22458de3609b7627 (patch)
treeda335283db08428468e74152ebe587c6de802e97
parentca06014c8de155d76b1da7eebb4be877cba40007 (diff)
downloadpylint-git-2a69387352bd1c941759faaa22458de3609b7627.tar.gz
Fix #5557: Don't emit `comparison-with-callable` if the callable raises (#5563)
* 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 <pierre.sassoulas@gmail.com> Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
-rw-r--r--ChangeLog5
-rw-r--r--doc/whatsnew/2.13.rst5
-rw-r--r--pylint/checkers/base.py20
-rw-r--r--tests/functional/c/comparison_with_callable.py11
-rw-r--r--tests/functional/c/comparison_with_callable_typing_constants.py18
5 files changed, 51 insertions, 8 deletions
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