diff --git a/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java index a62721dbde91..1acd71d36cda 100644 --- a/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java @@ -67,10 +67,7 @@ public class PyNoneFunctionAssignmentInspection extends PyInspection { } if (callable instanceof PyFunction) { final PyFunction function = (PyFunction)callable; - // Currently we don't infer types returned by decorators - if (hasInheritors(function) || - PyKnownDecoratorUtil.hasUnknownOrChangingReturnTypeDecorator(function, myTypeEvalContext) || - PyKnownDecoratorUtil.hasAbstractDecorator(function, myTypeEvalContext)) { + if (hasInheritors(function) || PyKnownDecoratorUtil.hasAbstractDecorator(function, myTypeEvalContext)) { return true; } } diff --git a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java index 1fc524d4595f..e5f2286104d9 100644 --- a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java +++ b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java @@ -216,7 +216,12 @@ public class PyKnownDecoratorUtil { public static boolean hasUnknownOrChangingReturnTypeDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) { final List decorators = getKnownDecorators(decoratable, context); - return !allDecoratorsAreKnown(decoratable, decorators) || decorators.contains(UNITTEST_MOCK_PATCH); + + if (!allDecoratorsAreKnown(decoratable, decorators)) { + return true; + } + + return ContainerUtil.exists(decorators, d -> d == UNITTEST_MOCK_PATCH || d == CONTEXTLIB_CONTEXTMANAGER); } public static boolean hasUnknownOrUpdatingAttributesDecorator(@NotNull PyDecoratable decoratable, @NotNull TypeEvalContext context) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index b0dccdebcace..3b937b89a03f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -217,6 +217,10 @@ public class PyFunctionImpl extends PyBaseElementImpl implements } } + if (getProperty() == null && PyKnownDecoratorUtil.hasUnknownOrChangingReturnTypeDecorator(this, context)) { + inferredType = PyUnionType.createWeakType(inferredType); + } + return PyTypingTypeProvider.toAsyncIfNeeded(this, inferredType); } diff --git a/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/a.py b/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/a.py new file mode 100644 index 000000000000..6f043fa08a20 --- /dev/null +++ b/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/a.py @@ -0,0 +1,13 @@ +from contextlib import contextmanager + + +@contextmanager +def mycm(): + pass + yield + pass + + +@mycm() +def decorated_func(): + pass \ No newline at end of file diff --git a/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/contextlib.py b/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/contextlib.py new file mode 100644 index 000000000000..c154c9fadd39 --- /dev/null +++ b/python/testData/inspections/PyCallingNonCallableInspection/FunctionDecoratedAsContextManager/contextlib.py @@ -0,0 +1,2 @@ +def contextmanager(func): + pass \ No newline at end of file diff --git a/python/testData/override/docstring.py b/python/testData/override/docstring.py index 3670ee1d0269..bc5c9a278d88 100644 --- a/python/testData/override/docstring.py +++ b/python/testData/override/docstring.py @@ -1,3 +1,6 @@ +from abc import abstractmethod + + class Abstract(object): @abstractmethod diff --git a/python/testData/override/docstring_after.py b/python/testData/override/docstring_after.py index a8b36960f349..2077a5d1bee7 100644 --- a/python/testData/override/docstring_after.py +++ b/python/testData/override/docstring_after.py @@ -1,3 +1,6 @@ +from abc import abstractmethod + + class Abstract(object): @abstractmethod diff --git a/python/testData/quickdoc/HoverOverManyDecoratorsFunction.html b/python/testData/quickdoc/HoverOverManyDecoratorsFunction.html index 924b39157377..a8f42cd905ed 100644 --- a/python/testData/quickdoc/HoverOverManyDecoratorsFunction.html +++ b/python/testData/quickdoc/HoverOverManyDecoratorsFunction.html @@ -1,3 +1,3 @@ @decorator1, @decorator2 def foo(param) -Inferred type: (param: Any) -> None \ No newline at end of file +Inferred type: (param: Any) -> Optional[Any] \ No newline at end of file diff --git a/python/testData/quickdoc/HoverOverOneDecoratorFunction.html b/python/testData/quickdoc/HoverOverOneDecoratorFunction.html index d6d10e520df3..e3cd274e7e24 100644 --- a/python/testData/quickdoc/HoverOverOneDecoratorFunction.html +++ b/python/testData/quickdoc/HoverOverOneDecoratorFunction.html @@ -1,3 +1,3 @@ @decorator def foo(param) -Inferred type: (param: Any) -> None \ No newline at end of file +Inferred type: (param: Any) -> Optional[Any] \ No newline at end of file diff --git a/python/testData/quickdoc/ManyDecoratorsFunction.html b/python/testData/quickdoc/ManyDecoratorsFunction.html index 9ce011667532..9ff32b5927c3 100644 --- a/python/testData/quickdoc/ManyDecoratorsFunction.html +++ b/python/testData/quickdoc/ManyDecoratorsFunction.html @@ -1 +1 @@ -@decorator1
@decorator2
def foo(param)
Inferred type: (param: Any) -> None
\ No newline at end of file +@decorator1
@decorator2
def foo(param)
Inferred type: (param: Any) -> Optional[Any]
\ No newline at end of file diff --git a/python/testData/quickdoc/Method.html b/python/testData/quickdoc/Method.html index 47fe337fd70e..08e764309caa 100644 --- a/python/testData/quickdoc/Method.html +++ b/python/testData/quickdoc/Method.html @@ -1 +1 @@ -class Foo

@deco
def meth(self)
Inferred type: (self: Foo) -> None

Doc of meth.
\ No newline at end of file +class Foo

@deco
def meth(self)
Inferred type: (self: Foo) -> Optional[Any]

Doc of meth.
\ No newline at end of file diff --git a/python/testData/quickdoc/OneDecoratorFunction.html b/python/testData/quickdoc/OneDecoratorFunction.html index ed2e15c38e67..e5731dd78a20 100644 --- a/python/testData/quickdoc/OneDecoratorFunction.html +++ b/python/testData/quickdoc/OneDecoratorFunction.html @@ -1 +1 @@ -@decorator
def foo(param)
Inferred type: (param: Any) -> None
\ No newline at end of file +@decorator
def foo(param)
Inferred type: (param: Any) -> Optional[Any]
\ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCallingNonCallableInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCallingNonCallableInspectionTest.java index a6ddd3904601..a197ca87b0bb 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyCallingNonCallableInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyCallingNonCallableInspectionTest.java @@ -108,6 +108,11 @@ public class PyCallingNonCallableInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-28626 + public void testFunctionDecoratedAsContextManager() { + doMultiFileTest(); + } + @NotNull @Override protected Class getInspectionClass() {