Skip to content

Commit

Permalink
Fix E1102 / not-callable false positive for property that returns…
Browse files Browse the repository at this point in the history
… 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]>
  • Loading branch information
4 people committed Apr 4, 2022
1 parent e7a0faf commit c48c45a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 10 deletions.
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Release date: TBA
* functions & classes which contain both a docstring and an ellipsis.
* A body which contains an ellipsis ``nodes.Expr`` node & at least one other statement.

* Only raise ``not-callable`` when all the inferred values of a property are not callable.

Closes #5931


What's New in Pylint 2.13.4?
Expand Down
4 changes: 4 additions & 0 deletions doc/whatsnew/2.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,7 @@ Other Changes
separated (e.g. by parens).

Closes #5769

* Only raise ``not-callable`` when all the inferred values of a property are not callable.

Closes #5931
22 changes: 12 additions & 10 deletions pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -1217,20 +1217,22 @@ def _check_uninferable_call(self, node):
# Decorated, see if it is decorated with a property.
# Also, check the returns and see if they are callable.
if decorated_with_property(attr):

try:
all_returns_are_callable = all(
return_node.callable() or return_node is astroid.Uninferable
for return_node in attr.infer_call_result(node)
)
call_results = list(attr.infer_call_result(node))
except astroid.InferenceError:
continue

if not all_returns_are_callable:
self.add_message(
"not-callable", node=node, args=node.func.as_string()
)
break
if all(
return_node is astroid.Uninferable for return_node in call_results
):
# We were unable to infer return values of the call, skipping
continue

if any(return_node.callable() for return_node in call_results):
# Only raise this issue if *all* the inferred values are not callable
continue

self.add_message("not-callable", node=node, args=node.func.as_string())

def _check_argument_order(self, node, call_site, called, called_param_names):
"""Match the supplied argument names against the function parameters.
Expand Down
24 changes: 24 additions & 0 deletions tests/functional/n/not_callable.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,27 @@ def get_number(arg):


get_number(10)() # [not-callable]

class Klass:
def __init__(self):
self._x = None

@property
def myproperty(self):
if self._x is None:
self._x = lambda: None
return self._x

myobject = Klass()
myobject.myproperty()

class Klass2:
@property
def something(self):
if __file__.startswith('s'):
return str

return 'abcd'

obj2 = Klass2()
obj2.something()

0 comments on commit c48c45a

Please sign in to comment.