From fb845a9ba2a366958ea26bc4c21f8f7062cbe5a4 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 1 Mar 2017 21:49:35 +0300 Subject: [PATCH] PY-22730 Don't render Optional[Any] as Union[None, Any] Specifically, handle "weak" union type only after checking that a union can be displayed as Optional. --- .../python/documentation/PyTypeModelBuilder.java | 16 +++++++++------- .../PyTypeCheckerInspection/ClassObjectType.py | 6 +++--- .../OptionalOfBoundTypeVarInWarnings.py | 11 +++++++++++ .../com/jetbrains/python/Py3TypeTest.java | 2 +- .../testSrc/com/jetbrains/python/PyTypeTest.java | 4 ++-- .../Py3TypeCheckerInspectionTest.java | 5 +++++ 6 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 80a36ad769e4..88240e22865e 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -17,6 +17,7 @@ package com.jetbrains.python.documentation; import com.google.common.collect.Collections2; import com.google.common.collect.Maps; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; @@ -268,7 +269,11 @@ public class PyTypeModelBuilder { else if (type instanceof PyUnionType && allowUnions) { final PyUnionType unionType = (PyUnionType)type; final Collection unionMembers = unionType.getMembers(); - if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false)) { + final Ref optionalType = getOptionalType(unionType); + if (optionalType != null) { + result = new OptionalType(build(optionalType.get(), true)); + } + else if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false)) { result = new UnknownType(build(unionType.excludeNull(myContext), true)); } else if (unionMembers.stream().allMatch(t -> t instanceof PyClassType && ((PyClassType)t).isDefinition())) { @@ -276,10 +281,7 @@ public class PyTypeModelBuilder { result = new ClassObjectType(new OneOf(instanceTypes)); } else { - result = Optional - .ofNullable(getOptionalType(unionType)) - .map(optionalType -> new OptionalType(build(optionalType, true))) - .orElseGet(() -> new OneOf(Collections2.transform(unionMembers, t -> build(t, false)))); + result = new OneOf(Collections2.transform(unionMembers, t -> build(t, false))); } } else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) { @@ -318,7 +320,7 @@ public class PyTypeModelBuilder { } @Nullable - private static PyType getOptionalType(@NotNull PyUnionType type) { + private static Ref getOptionalType(@NotNull PyUnionType type) { final Collection members = type.getMembers(); if (members.size() == 2) { boolean foundNone = false; @@ -332,7 +334,7 @@ public class PyTypeModelBuilder { } } if (foundNone) { - return optional; + return Ref.create(optional); } } return null; diff --git a/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py index e30edbc21fda..9a829ed33c98 100644 --- a/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py +++ b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py @@ -31,6 +31,6 @@ def expects_myclass_descendant_or_none(x: Optional[Type[T2]]): pass expects_myclass_descendant_or_none(MyClass) -expects_myclass_descendant_or_none(MyClass()) -expects_myclass_descendant_or_none(object) -expects_myclass_descendant_or_none(object()) \ No newline at end of file +expects_myclass_descendant_or_none(MyClass()) +expects_myclass_descendant_or_none(object) +expects_myclass_descendant_or_none(object()) \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py b/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py new file mode 100644 index 000000000000..64cee3d5ba3b --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py @@ -0,0 +1,11 @@ +from typing import Optional, TypeVar + + +T = TypeVar('T', int) + + +def expects_int_subclass_or_none(x: Optional[T]): + pass + + +expects_int_subclass_or_none('foo') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index f948e98cd602..ff5dd8a79fe0 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -450,7 +450,7 @@ public class Py3TypeTest extends PyTestCase { // PY-20757 public void testMinElseNone() { - doTest("Union[None, Any]", + doTest("Optional[Any]", "def get_value(v):\n" + " if v:\n" + " return min(v)\n" + diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index c57ba0b361e1..f75e723a94d9 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1496,14 +1496,14 @@ public class PyTypeTest extends PyTestCase { // PY-20409 public void testGetFromDictWithDefaultNoneValue() { - doTest("Union[None, Any]", + doTest("Optional[Any]", "d = {}\n" + "expr = d.get(\"abc\", None)"); } // PY-20757 public void testMinOrNone() { - doTest("Union[None, Any]", + doTest("Optional[Any]", "def get_value(v):\n" + " if v:\n" + " return min(v)\n" + diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index fd89036b5c53..9fbc9fe97bc7 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -213,4 +213,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testClassObjectTypeWithUnion() { doTest(); } + + // PY-22730 + public void testOptionalOfBoundTypeVarInWarnings() { + doTest(); + } }