diff --git a/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java index 0e69c624de1d..af5de05d7450 100644 --- a/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPropertyDefinitionInspection.java @@ -120,13 +120,13 @@ public class PyPropertyDefinitionInspection extends PyInspection { if (resolved instanceof PyFunction) callable = (PyFunction)resolved; else if (resolved instanceof PyLambdaExpression) callable = (PyLambdaExpression)resolved; else { - reportStrangeArg(resolved, argument); + reportNonCallableArg(resolved, argument); continue; } } else if (argument instanceof PyLambdaExpression) callable = (PyLambdaExpression)argument; else if (! "doc".equals(param_name)) { - reportStrangeArg(argument, argument); + reportNonCallableArg(argument, argument); continue; } if ("fget".equals(param_name)) checkGetter(callable, argument); @@ -152,9 +152,18 @@ public class PyPropertyDefinitionInspection extends PyInspection { }, false); } - void reportStrangeArg(PsiElement resolved, PsiElement being_checked) { + private void reportNonCallableArg(PsiElement resolved, PsiElement being_checked) { if (! PyUtil.instanceOf(resolved, PySubscriptionExpression.class, PyNoneLiteralExpression.class)) { - registerProblem(being_checked, PyBundle.message("INSP.strange.arg.want.callable")); + boolean is_not_callable = true; + if (resolved instanceof PyExpression) { + PyType expr_type = ((PyExpression)resolved).getType(myTypeEvalContext); + if (expr_type instanceof PyClassType) { + final PyClassType cls_type = (PyClassType)expr_type; + PyClass cls = cls_type.getPyClass(); + if (cls != null && !cls_type.isDefinition()) is_not_callable = cls.findMethodByName("__call__", true) == null; + } + } + if (is_not_callable) registerProblem(being_checked, PyBundle.message("INSP.strange.arg.want.callable")); } } diff --git a/python/testData/inspections/PyPropertyDefinitionInspection25/expected.xml b/python/testData/inspections/PyPropertyDefinitionInspection25/expected.xml index 38414607ac1f..400806dc8848 100644 --- a/python/testData/inspections/PyPropertyDefinitionInspection25/expected.xml +++ b/python/testData/inspections/PyPropertyDefinitionInspection25/expected.xml @@ -45,5 +45,10 @@ 48 Getter should return something + + prop_test.py + 76 + Strange argument; a callable expected + diff --git a/python/testData/inspections/PyPropertyDefinitionInspection25/src/prop_test.py b/python/testData/inspections/PyPropertyDefinitionInspection25/src/prop_test.py index 48bcf70e200b..3590ccefe94f 100644 --- a/python/testData/inspections/PyPropertyDefinitionInspection25/src/prop_test.py +++ b/python/testData/inspections/PyPropertyDefinitionInspection25/src/prop_test.py @@ -64,3 +64,14 @@ class A(object): def bar(self): return None +class Ghostbusters(object): + def __call__(self): + return "Who do you call?" + +gb = Ghostbusters() + +class B(object): + x = property(gb) # pass + y = property(Ghostbusters()) # pass + z = property(Ghostbusters) # fail: not callable +