Skip to content

Commit 79f3e1d

Browse files
tushar-deepsourcejacobtylerwallsDanielNoordPierre-Sassoulas
authored
Fix E1102 / not-callable false positive for property that returns a lambda function conditionally (#6068)
Co-authored-by: Jacob Walls <[email protected]> Co-authored-by: Daniël van Noord <[email protected]> Co-authored-by: Pierre Sassoulas <[email protected]>
1 parent dfe5419 commit 79f3e1d

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ Release date: TBA
5353
* functions & classes which contain both a docstring and an ellipsis.
5454
* A body which contains an ellipsis ``nodes.Expr`` node & at least one other statement.
5555

56+
* Only raise ``not-callable`` when all the inferred values of a property are not callable.
57+
58+
Closes #5931
5659

5760

5861
What's New in Pylint 2.13.4?

doc/whatsnew/2.14.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Extensions
3838
Other Changes
3939
=============
4040

41+
* Only raise ``not-callable`` when all the inferred values of a property are not callable.
42+
43+
Closes #5931
44+
4145
* The concept of checker priority has been removed.
4246

4347
* Fix false negative for ``no-member`` when attempting to assign an instance

pylint/checkers/typecheck.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,20 +1226,22 @@ def _check_uninferable_call(self, node):
12261226
# Decorated, see if it is decorated with a property.
12271227
# Also, check the returns and see if they are callable.
12281228
if decorated_with_property(attr):
1229-
12301229
try:
1231-
all_returns_are_callable = all(
1232-
return_node.callable() or return_node is astroid.Uninferable
1233-
for return_node in attr.infer_call_result(node)
1234-
)
1230+
call_results = list(attr.infer_call_result(node))
12351231
except astroid.InferenceError:
12361232
continue
12371233

1238-
if not all_returns_are_callable:
1239-
self.add_message(
1240-
"not-callable", node=node, args=node.func.as_string()
1241-
)
1242-
break
1234+
if all(
1235+
return_node is astroid.Uninferable for return_node in call_results
1236+
):
1237+
# We were unable to infer return values of the call, skipping
1238+
continue
1239+
1240+
if any(return_node.callable() for return_node in call_results):
1241+
# Only raise this issue if *all* the inferred values are not callable
1242+
continue
1243+
1244+
self.add_message("not-callable", node=node, args=node.func.as_string())
12431245

12441246
def _check_argument_order(self, node, call_site, called, called_param_names):
12451247
"""Match the supplied argument names against the function parameters.

tests/functional/n/not_callable.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,27 @@ def get_number(arg):
200200

201201

202202
get_number(10)() # [not-callable]
203+
204+
class Klass:
205+
def __init__(self):
206+
self._x = None
207+
208+
@property
209+
def myproperty(self):
210+
if self._x is None:
211+
self._x = lambda: None
212+
return self._x
213+
214+
myobject = Klass()
215+
myobject.myproperty()
216+
217+
class Klass2:
218+
@property
219+
def something(self):
220+
if __file__.startswith('s'):
221+
return str
222+
223+
return 'abcd'
224+
225+
obj2 = Klass2()
226+
obj2.something()

0 commit comments

Comments
 (0)