Skip to content

Commit 2a69387

Browse files
jacobtylerwallsPierre-SassoulasDanielNoord
authored
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 <[email protected]> Co-authored-by: Daniël van Noord <[email protected]>
1 parent ca06014 commit 2a69387

File tree

5 files changed

+51
-8
lines changed

5 files changed

+51
-8
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ Release date: TBA
6363

6464
Closes #3675
6565

66+
* Fix ``comparison-with-callable`` false positive for callables that raise, such
67+
as typing constants.
68+
69+
Closes #5557
70+
6671
* Fix ``unnecessary_dict_index_lookup`` false positive when deleting a dictionary's entry.
6772

6873
Closes #4716

doc/whatsnew/2.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ Other Changes
9999

100100
* ``fatal`` was added to the variables permitted in score evaluation expressions.
101101

102+
* Fix ``comparison-with-callable`` false positive for callables that raise, such
103+
as typing constants.
104+
105+
Closes #5557
106+
102107
* The ``PyLinter`` class will now be initialized with a ``TextReporter``
103108
as its reporter if none is provided.
104109

pylint/checkers/base.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,14 +2511,18 @@ def _check_callable_comparison(self, node):
25112511
left_operand, right_operand = node.left, node.ops[0][1]
25122512
# this message should be emitted only when there is comparison of bare callable
25132513
# with non bare callable.
2514-
if (
2515-
sum(
2516-
1
2517-
for operand in (left_operand, right_operand)
2518-
if isinstance(utils.safe_infer(operand), bare_callables)
2519-
)
2520-
== 1
2521-
):
2514+
number_of_bare_callables = 0
2515+
for operand in left_operand, right_operand:
2516+
inferred = utils.safe_infer(operand)
2517+
# Ignore callables that raise, as well as typing constants
2518+
# implemented as functions (that raise via their decorator)
2519+
if (
2520+
isinstance(inferred, bare_callables)
2521+
and "typing._SpecialForm" not in inferred.decoratornames()
2522+
and not any(isinstance(x, nodes.Raise) for x in inferred.body)
2523+
):
2524+
number_of_bare_callables += 1
2525+
if number_of_bare_callables == 1:
25222526
self.add_message("comparison-with-callable", node=node)
25232527

25242528
@utils.check_messages(

tests/functional/c/comparison_with_callable.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,14 @@ def fake_property(self, prop):
5858
b = 786
5959
if a == b:
6060
pass
61+
62+
63+
def eventually_raise():
64+
print()
65+
raise Exception
66+
67+
68+
if a == eventually_raise:
69+
# Does not emit comparison-with-callable because the
70+
# function (eventually) raises
71+
pass
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""Typing constants are actually implemented as functions, but they
2+
raise when called, so Pylint uses that to avoid false positives for
3+
comparison-with-callable.
4+
"""
5+
from typing import Any, Optional
6+
7+
8+
def check_any(type_) -> bool:
9+
"""See https://github.com/PyCQA/pylint/issues/5557"""
10+
return type_ == Any
11+
12+
13+
def check_optional(type_) -> bool:
14+
"""
15+
Unlike Any, Optional does not raise in its body.
16+
It raises via its decorator: typing._SpecialForm.__call__()
17+
"""
18+
return type_ == Optional

0 commit comments

Comments
 (0)