From 0d3f684496a7b199698dee218e3e0383b0ba747b Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 18 Aug 2017 15:26:34 +0300 Subject: [PATCH] Support typing.Supports* in PyABCUtil. PY-24789, PY-23391, PY-24194, PY-23289 Fixed. --- .../src/com/jetbrains/python/PyNames.java | 18 ++++++--- .../typing/PyTypingTypeProvider.java | 6 +++ .../jetbrains/python/psi/types/PyABCUtil.java | 22 ++++++++--- .../PyTypeCheckerInspection/TypingSupports.py | 37 +++++++++++++++++++ .../Py3TypeCheckerInspectionTest.java | 8 ++++ 5 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/TypingSupports.py diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java index 3fda5ece8f5a..458077a48b6e 100644 --- a/python/psi-api/src/com/jetbrains/python/PyNames.java +++ b/python/psi-api/src/com/jetbrains/python/PyNames.java @@ -183,6 +183,12 @@ public class PyNames { public static final String SIZEOF = "__sizeof__"; public static final String INIT_SUBCLASS = "__init_subclass__"; public static final String FSPATH = "__fspath__"; + public static final String COMPLEX = "__complex__"; + public static final String FLOAT = "__float__"; + public static final String INT = "__int__"; + public static final String BYTES = "__bytes__"; + public static final String ABS = "__abs__"; + public static final String ROUND = "__round__"; public static final String NAME = "__name__"; public static final String ENTER = "__enter__"; @@ -295,7 +301,7 @@ public class PyNames { private static final BuiltinDescription _exit_descr = new BuiltinDescription("(self, exc_type, exc_val, exc_tb)"); private static final ImmutableMap BuiltinMethods = ImmutableMap.builder() - .put("__abs__", _only_self_descr) + .put(ABS, _only_self_descr) .put("__add__", _self_other_descr) .put("__and__", _self_other_descr) //_BuiltinMethods.put("__all__", _only_self_descr); @@ -306,7 +312,7 @@ public class PyNames { //_BuiltinMethods.put("__class__", _only_self_descr); .put("__cmp__", _self_other_descr) .put("__coerce__", _self_other_descr) - .put("__complex__", _only_self_descr) + .put(COMPLEX, _only_self_descr) .put("__contains__", _self_item_descr) .put("__copy__", _only_self_descr) //_BuiltinMethods.put("__debug__", _only_self_descr); @@ -324,7 +330,7 @@ public class PyNames { .put("__exit__", _exit_descr) .put("__eq__", _self_other_descr) //_BuiltinMethods.put("__file__", _only_self_descr); - .put("__float__", _only_self_descr) + .put(FLOAT, _only_self_descr) .put("__floor__", _only_self_descr) .put("__floordiv__", _self_other_descr) //_BuiltinMethods.put("__future__", _only_self_descr); @@ -350,7 +356,7 @@ public class PyNames { .put("__imul__", _self_other_descr) .put("__index__", _only_self_descr) .put(INIT, _only_self_descr) - .put("__int__", _only_self_descr) + .put(INT, _only_self_descr) .put("__invert__", _only_self_descr) .put("__ior__", _self_other_descr) .put("__ipow__", _self_other_descr) @@ -425,11 +431,11 @@ public class PyNames { public static final ImmutableMap PY3_BUILTIN_METHODS = ImmutableMap.builder() .putAll(BuiltinMethods) .put("__bool__", _only_self_descr) - .put("__bytes__", _only_self_descr) + .put(BYTES, _only_self_descr) .put("__format__", new BuiltinDescription("(self, format_spec)")) .put("__instancecheck__", new BuiltinDescription("(self, instance)")) .put("__prepare__", new BuiltinDescription("(metacls, name, bases)")) - .put("__round__", new BuiltinDescription("(self, n=None)")) + .put(ROUND, new BuiltinDescription("(self, n=None)")) .put("__subclasscheck__", new BuiltinDescription("(self, subclass)")) .put(DUNDER_NEXT, _only_self_descr) .build(); diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index 4935120ff5e9..ff737c43c6ed 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -75,6 +75,12 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { private static final String CALLABLE = "typing.Callable"; public static final String NAMEDTUPLE_SIMPLE = "NamedTuple"; + public static final String SUPPORTS_INT_SIMPLE = "SupportsInt"; + public static final String SUPPORTS_FLOAT_SIMPLE = "SupportsFloat"; + public static final String SUPPORTS_COMPLEX_SIMPLE = "SupportsComplex"; + public static final String SUPPORTS_BYTES_SIMPLE = "SupportsBytes"; + public static final String SUPPORTS_ABS_SIMPLE = "SupportsAbs"; + public static final String SUPPORTS_ROUND_SIMPLE = "SupportsRound"; public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)"); diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java index 68c2a541508f..e98e4000dc4e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java +++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java @@ -16,6 +16,7 @@ package com.jetbrains.python.psi.types; import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.PyClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -82,14 +83,14 @@ public class PyABCUtil { hasMethod(subClass, "discard", inherited, context) && hasMethod(subClass, "add", inherited, context); } - if (PyNames.ABC_COMPLEX.equals(superClassName)) { - return hasMethod(subClass, "__complex__", inherited, context); + if (PyNames.ABC_COMPLEX.equals(superClassName) || PyTypingTypeProvider.SUPPORTS_COMPLEX_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.COMPLEX, inherited, context); } - if (PyNames.ABC_REAL.equals(superClassName)) { - return hasMethod(subClass, "__float__", inherited, context); + if (PyNames.ABC_REAL.equals(superClassName) || PyTypingTypeProvider.SUPPORTS_FLOAT_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.FLOAT, inherited, context); } - if (PyNames.ABC_INTEGRAL.equals(superClassName)) { - return hasMethod(subClass, "__int__", inherited, context); + if (PyNames.ABC_INTEGRAL.equals(superClassName) || PyTypingTypeProvider.SUPPORTS_INT_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.INT, inherited, context); } if (PyNames.ABC_NUMBER.equals(superClassName) && "Decimal".equals(subClass.getName())) { return true; @@ -100,6 +101,15 @@ public class PyABCUtil { if (PyNames.PATH_LIKE.equals(superClassName)) { return hasMethod(subClass, PyNames.FSPATH, inherited, context); } + if (PyTypingTypeProvider.SUPPORTS_BYTES_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.BYTES, inherited, context); + } + if (PyTypingTypeProvider.SUPPORTS_ABS_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.ABS, inherited, context); + } + if (PyTypingTypeProvider.SUPPORTS_ROUND_SIMPLE.equals(superClassName)) { + return hasMethod(subClass, PyNames.ROUND, inherited, context); + } return false; } diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypingSupports.py b/python/testData/inspections/PyTypeCheckerInspection/TypingSupports.py new file mode 100644 index 000000000000..92dc18604030 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/TypingSupports.py @@ -0,0 +1,37 @@ +import typing + + +def check_complex(p: typing.SupportsComplex): + print(p.__complex__()) + + +class A: + def __int__(self): + return 5 + + + def __float__(self): + return 5.0 + + + def __complex__(self): + return complex(5.0, 0.0) + + def __bytes__(self): + return b'bytes' + + def __abs__(self): + return 5 + + def __round__(self, n=None): + return 5 + + +a = A() +print(int(a)) +print(float(a)) +check_complex(a) +print(bytes(a)) +print(abs(a)) +print(round(a)) + diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 1ebb42943c46..638ad3f34abc 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -260,4 +260,12 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testUnionOfIntAndFloatShouldBeConsideredAsDividable() { doTest(); } + + // PY-23289 + // PY-23391 + // PY-24194 + // PY-24789 + public void testTypingSupports() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } }