diff --git a/python/helpers/pycharm_generator_utils/constants.py b/python/helpers/pycharm_generator_utils/constants.py index 066cc1c4cc39..68aa57ff7fea 100644 --- a/python/helpers/pycharm_generator_utils/constants.py +++ b/python/helpers/pycharm_generator_utils/constants.py @@ -6,7 +6,7 @@ import string import time # !!! Don't forget to update VERSION and required_gen_version if necessary !!! -VERSION = "1.143" +VERSION = "1.144" OUT_ENCODING = 'utf-8' diff --git a/python/helpers/pycharm_generator_utils/module_redeclarator.py b/python/helpers/pycharm_generator_utils/module_redeclarator.py index fd34c3ad7c37..5bde7733589e 100644 --- a/python/helpers/pycharm_generator_utils/module_redeclarator.py +++ b/python/helpers/pycharm_generator_utils/module_redeclarator.py @@ -1015,6 +1015,8 @@ class ModuleRedeclarator(object): if self.doing_builtins and p_name == BUILTIN_MOD_NAME: txt = create_generator() self.classes_buf.out(0, txt) + txt = create_async_generator() + self.classes_buf.out(0, txt) txt = create_function() self.classes_buf.out(0, txt) txt = create_method() diff --git a/python/helpers/pycharm_generator_utils/util_methods.py b/python/helpers/pycharm_generator_utils/util_methods.py index 714817620886..bd5bd7ba7ebd 100644 --- a/python/helpers/pycharm_generator_utils/util_methods.py +++ b/python/helpers/pycharm_generator_utils/util_methods.py @@ -76,6 +76,42 @@ class __generator(object): """ return txt +def create_async_generator(): + # Fake + txt = """ +class __asyncgenerator(object): + '''A mock class representing the async generator function type.''' + def __init__(self): + '''Create an async generator object.''' + self.__name__ = '' + self.__qualname__ = '' + self.ag_await = None + self.ag_frame = None + self.ag_running = False + self.ag_code = None + + def __aiter__(self): + '''Defined to support iteration over container.''' + pass + + def __anext__(self): + '''Returns an awaitable, that performs one asynchronous generator iteration when awaited.''' + pass + + def aclose(self): + '''Returns an awaitable, that throws a GeneratorExit exception into generator.''' + pass + + def asend(self, value): + '''Returns an awaitable, that pushes the value object in generator.''' + pass + + def athrow(self, type, value=None, traceback=None): + '''Returns an awaitable, that throws an exception into generator.''' + pass +""" + return txt + def create_function(): txt = """ class __function(object): diff --git a/python/helpers/python-skeletons/builtins.py b/python/helpers/python-skeletons/builtins.py index ce025ea47774..69680d5290ea 100644 --- a/python/helpers/python-skeletons/builtins.py +++ b/python/helpers/python-skeletons/builtins.py @@ -2480,6 +2480,59 @@ class __coroutine(object): pass +class __asyncgenerator(object): + """A mock class representing the async generator function type.""" + def __init__(self): + """Create an async generator object. + + :rtype: __asyncgenerator[T, U] + """ + self.__name__ = '' + self.__qualname__ = '' + self.ag_await = None + self.ag_frame = None + self.ag_running = False + self.ag_code = None + + def __aiter__(self): + """Defined to support iteration over container. + + :rtype: collections.AsyncIterator[T] + """ + pass + + def __anext__(self): + """Returns an awaitable, that performs one asynchronous generator + iteration when awaited. + + :rtype: collections.Awaitable[T] + """ + pass + + def aclose(self): + """Returns an awaitable, that throws a GeneratorExit exception + into generator. + + :rtype: collections.Awaitable[T] + """ + pass + + def asend(self, value): + """Returns an awaitable, that pushes the value object in generator. + + :type value: U + :rtype: collections.Awaitable[T] + """ + pass + + def athrow(self, type, value=None, traceback=None): + """Returns an awaitable, that throws an exception into generator. + + :rtype: collections.Awaitable[T] + """ + pass + + class __function(object): """A mock class representing function type.""" diff --git a/python/helpers/required_gen_version b/python/helpers/required_gen_version index b424e69dd84b..74e41292c809 100644 --- a/python/helpers/required_gen_version +++ b/python/helpers/required_gen_version @@ -6,7 +6,7 @@ (default) 1.127 # anything not explicitly marked -(built-in) 1.138 # skeletons of all built-in modules are built together +(built-in) 1.144 # skeletons of all built-in modules are built together # Note: modules like itertools, etc are "(built-in)" and are ignored if given separately _fileio 1.127 diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java index 651e67039aeb..13c091ba06c0 100644 --- a/python/psi-api/src/com/jetbrains/python/PyNames.java +++ b/python/psi-api/src/com/jetbrains/python/PyNames.java @@ -94,6 +94,7 @@ public class PyNames { public static final String FAKE_METHOD = "__method"; public static final String FAKE_NAMEDTUPLE = "__namedtuple"; public static final String FAKE_COROUTINE = "__coroutine"; + public static final String FAKE_ASYNC_GENERATOR = "__asyncgenerator"; public static final String FUTURE_MODULE = "__future__"; public static final String UNICODE_LITERALS = "unicode_literals"; @@ -142,7 +143,8 @@ public class PyNames { public static final String MUTABLE_MAPPING = "MutableMapping"; public static final String ABC_SET = "Set"; public static final String ABC_MUTABLE_SET = "MutableSet"; - + + public static final String AWAITABLE = "Awaitable"; public static final String ASYNC_ITERABLE = "AsyncIterable"; public static final String ABC_NUMBER = "Number"; diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 2909171939fe..12bc421730d3 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -187,7 +187,7 @@ public class PyFunctionImpl extends PyBaseElementImpl implements @Override public PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { final PyType type = getReturnType(context); - return isAsync() && isAsyncAllowed() ? createCoroutineType(type) : type; + return isAsync() && isAsyncAllowed() ? createCoroutineType(type, context) : type; } @Nullable @@ -375,10 +375,22 @@ public class PyFunctionImpl extends PyBaseElementImpl implements } @Nullable - private PyType createCoroutineType(@Nullable PyType returnType) { + private PyType createCoroutineType(@Nullable PyType returnType, @NotNull TypeEvalContext context) { final PyBuiltinCache cache = PyBuiltinCache.getInstance(this); - final PyClass generator = cache.getClass(PyNames.FAKE_COROUTINE); - return generator != null ? new PyCollectionTypeImpl(generator, false, Collections.singletonList(returnType)) : null; + + if (returnType instanceof PyCollectionType && PyNames.FAKE_GENERATOR.equals(returnType.getName())) { + final PyClass asyncGenerator = cache.getClass(PyNames.FAKE_ASYNC_GENERATOR); + final List generatorElementTypes = ((PyCollectionType)returnType).getElementTypes(context); + + if (asyncGenerator == null || generatorElementTypes.isEmpty()) { + return null; + } + + return new PyCollectionTypeImpl(asyncGenerator, false, Arrays.asList(generatorElementTypes.get(0), null)); + } + + final PyClass coroutine = cache.getClass(PyNames.FAKE_COROUTINE); + return coroutine != null ? new PyCollectionTypeImpl(coroutine, false, Collections.singletonList(returnType)) : null; } public PyFunction asMethod() { diff --git a/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java index aab8cd681e10..03db867a6942 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyPrefixExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiReference; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -162,6 +163,11 @@ public class PyPrefixExpressionImpl extends PyElementImpl implements PyPrefixExp } } } + else if (type instanceof PyClassType && + type instanceof PyCollectionType && + PyNames.AWAITABLE.equals(((PyClassType)type).getPyClass().getName())) { + return ContainerUtil.getFirstItem(((PyCollectionType)type).getElementTypes(context)); + } } else if (type instanceof PyUnionType) { final List memberReturnTypes = new ArrayList<>(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index ec27e5b09fc8..c1cc7d6608a8 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -386,6 +386,16 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl len(other) and self.__ge__(other) def __ge__(self, other): if not isinstance(other, Set): return NotImplemented - return other.__le__(self) + if len(self) < len(other): + return False + for elem in other: + if elem not in self: + return False + return True def __eq__(self, other): if not isinstance(other, Set): return NotImplemented return len(self) == len(other) and self.__le__(other) - def __ne__(self, other): - return not (self == other) - @classmethod def _from_iterable(cls, it): '''Construct an instance of the class from any iterable input. @@ -236,6 +411,8 @@ class Set(Sized, Iterable, Container): return NotImplemented return self._from_iterable(value for value in other if value in self) + __rand__ = __and__ + def isdisjoint(self, other): 'Return True if two sets have a null intersection.' for value in other: @@ -249,6 +426,8 @@ class Set(Sized, Iterable, Container): chain = (e for s in (self, other) for e in s) return self._from_iterable(chain) + __ror__ = __or__ + def __sub__(self, other): if not isinstance(other, Set): if not isinstance(other, Iterable): @@ -257,6 +436,14 @@ class Set(Sized, Iterable, Container): return self._from_iterable(value for value in self if value not in other) + def __rsub__(self, other): + if not isinstance(other, Set): + if not isinstance(other, Iterable): + return NotImplemented + other = self._from_iterable(other) + return self._from_iterable(value for value in other + if value not in self) + def __xor__(self, other): if not isinstance(other, Set): if not isinstance(other, Iterable): @@ -264,6 +451,8 @@ class Set(Sized, Iterable, Container): other = self._from_iterable(other) return (self - other) | (other - self) + __rxor__ = __xor__ + def _hash(self): """Compute the hash value of a set. @@ -432,14 +621,13 @@ class Mapping(Sized, Iterable, Container): return NotImplemented return dict(self.items()) == dict(other.items()) - def __ne__(self, other): - return not (self == other) - Mapping.register(mappingproxy) class MappingView(Sized): + __slots__ = '_mapping', + def __init__(self, mapping): self._mapping = mapping @@ -452,6 +640,8 @@ class MappingView(Sized): class KeysView(MappingView, Set): + __slots__ = () + @classmethod def _from_iterable(self, it): return set(it) @@ -467,6 +657,8 @@ KeysView.register(dict_keys) class ItemsView(MappingView, Set): + __slots__ = () + @classmethod def _from_iterable(self, it): return set(it) @@ -489,6 +681,8 @@ ItemsView.register(dict_items) class ValuesView(MappingView): + __slots__ = () + def __contains__(self, value): for key in self._mapping: if value == self._mapping[key]: @@ -565,23 +759,24 @@ class MutableMapping(Mapping): If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v ''' - if len(args) > 2: - raise TypeError("update() takes at most 2 positional " - "arguments ({} given)".format(len(args))) - elif not args: - raise TypeError("update() takes at least 1 argument (0 given)") - self = args[0] - other = args[1] if len(args) >= 2 else () - - if isinstance(other, Mapping): - for key in other: - self[key] = other[key] - elif hasattr(other, "keys"): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value + if not args: + raise TypeError("descriptor 'update' of 'MutableMapping' object " + "needs an argument") + self, *args = args + if len(args) > 1: + raise TypeError('update expected at most 1 arguments, got %d' % + len(args)) + if args: + other = args[0] + if isinstance(other, Mapping): + for key in other: + self[key] = other[key] + elif hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value for key, value in kwds.items(): self[key] = value @@ -633,13 +828,23 @@ class Sequence(Sized, Iterable, Container): for i in reversed(range(len(self))): yield self[i] - def index(self, value): - '''S.index(value) -> integer -- return first index of value. + def index(self, value, start=0, stop=None): + '''S.index(value, [start, [stop]]) -> integer -- return first index of value. Raises ValueError if the value is not present. ''' - for i, v in enumerate(self): - if v == value: - return i + if start is not None and start < 0: + start = max(len(self) + start, 0) + if stop is not None and stop < 0: + stop += len(self) + + i = start + while stop is None or i < stop: + try: + if self[i] == value: + return i + except IndexError: + break + i += 1 raise ValueError def count(self, value): diff --git a/python/testData/MockSdk3.4/python_stubs/builtins.py b/python/testData/MockSdk3.4/python_stubs/builtins.py index 08bc4211384d..2d89595fbbfd 100644 --- a/python/testData/MockSdk3.4/python_stubs/builtins.py +++ b/python/testData/MockSdk3.4/python_stubs/builtins.py @@ -1,7 +1,7 @@ # encoding: utf-8 # module builtins # from (built-in) -# by generator 1.138 +# by generator 1.144 """ Built-in functions, exceptions, and other objects. @@ -127,7 +127,7 @@ def dir(p_object=None): # real signature unknown; restored from __doc__ return [] def divmod(x, y): # known case of builtins.divmod - """ Return the tuple ((x-x%y)/y, x%y). Invariant: div*y + mod == x. """ + """ Return the tuple (x//y, x%y). Invariant: div*y + mod == x. """ return (0, 0) def eval(*args, **kwargs): # real signature unknown @@ -602,6 +602,38 @@ class __generator(object): pass +class __asyncgenerator(object): + '''A mock class representing the async generator function type.''' + def __init__(self): + '''Create an async generator object.''' + self.__name__ = '' + self.__qualname__ = '' + self.ag_await = None + self.ag_frame = None + self.ag_running = False + self.ag_code = None + + def __aiter__(self): + '''Defined to support iteration over container.''' + pass + + def __anext__(self): + '''Returns an awaitable, that performs one asynchronous generator iteration when awaited.''' + pass + + def aclose(self): + '''Returns an awaitable, that throws a GeneratorExit exception into generator.''' + pass + + def asend(self, value): + '''Returns an awaitable, that pushes the value object in generator.''' + pass + + def athrow(self, type, value=None, traceback=None): + '''Returns an awaitable, that throws an exception into generator.''' + pass + + class __function(object): '''A mock class representing function type.''' @@ -1338,7 +1370,7 @@ class bytearray(object): bytearray(int) -> bytes array of size given by the parameter initialized with null bytes bytearray() -> empty bytes array - Construct an mutable bytearray object from: + Construct a mutable bytearray object from: - an iterable yielding integers in range(256) - a text string encoded using the specified encoding - a bytes or a buffer object @@ -1859,7 +1891,7 @@ class bytearray(object): bytearray(int) -> bytes array of size given by the parameter initialized with null bytes bytearray() -> empty bytes array - Construct an mutable bytearray object from: + Construct a mutable bytearray object from: - an iterable yielding integers in range(256) - a text string encoded using the specified encoding - a bytes or a buffer object diff --git a/python/testData/highlighting/nonEmptyReturnInsideAsyncDef.py b/python/testData/highlighting/nonEmptyReturnInsideAsyncDef.py new file mode 100644 index 000000000000..9d1005b31743 --- /dev/null +++ b/python/testData/highlighting/nonEmptyReturnInsideAsyncDef.py @@ -0,0 +1,10 @@ +async def foo(x): + await x + yield x + return x + + +async def bar(x): + await x + yield x + return diff --git a/python/testData/inspections/PyTypeCheckerInspection/AsyncForOverAsyncGenerator.py b/python/testData/inspections/PyTypeCheckerInspection/AsyncForOverAsyncGenerator.py new file mode 100644 index 000000000000..526921cd76f3 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/AsyncForOverAsyncGenerator.py @@ -0,0 +1,7 @@ +async def asyncgen(): + yield 10 + + +async def run(): + async for i in asyncgen(): + print(i) \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/ForOverAsyncGenerator.py b/python/testData/inspections/PyTypeCheckerInspection/ForOverAsyncGenerator.py new file mode 100644 index 000000000000..da51eacac84f --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/ForOverAsyncGenerator.py @@ -0,0 +1,7 @@ +async def asyncgen(): + yield 10 + + +async def run(): + for i in asyncgen(): + print(i) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java index 678fa212c5ef..e4bc94b311a1 100644 --- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java +++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java @@ -165,6 +165,25 @@ public class Py3CompletionTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest); } + // PY-20770 + public void testAsyncGenerator() { + runWithLanguageLevel( + LanguageLevel.PYTHON36, + () -> { + final String asyncGenerator = "async def asyncgen():\n" + + " yield 42\n"; + + assertContainsElements(doTestByText(asyncGenerator + + "asyncgen().__a\n"), + PyNames.AITER, PyNames.ANEXT); + + assertContainsElements(doTestByText(asyncGenerator + + "asyncgen().a\n"), + "ag_await", "ag_frame", "ag_running", "ag_code", "aclose", "asend", "athrow"); + } + ); + } + @Override protected String getTestDataPath() { return super.getTestDataPath() + "/completion"; diff --git a/python/testSrc/com/jetbrains/python/Py3HighlightingTest.java b/python/testSrc/com/jetbrains/python/Py3HighlightingTest.java new file mode 100644 index 000000000000..ad24ba769856 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/Py3HighlightingTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python; + +import com.intellij.testFramework.LightProjectDescriptor; +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Py3HighlightingTest extends PyTestCase { + + @NotNull + private static final String TEST_PATH = "/highlighting/"; + + @Nullable + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return ourPy3Descriptor; + } + + // PY-20770 + public void testNonEmptyReturnInsideAsyncDef() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest(true, false)); + } + + private void doTest(boolean checkWarnings, boolean checkInfos) { + myFixture.testHighlighting(checkWarnings, checkInfos, false, TEST_PATH + getTestName(true) + PyNames.DOT_PY); + } +} diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index 22486606a105..e63fb9fd6571 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -197,6 +197,66 @@ public class Py3TypeTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, () -> doTest("bytes", "expr = b'foo'")); } + // PY-20770 + public void testAsyncGenerator() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("__asyncgenerator[int, Any]", + "async def asyncgen():\n" + + " yield 42\n" + + "expr = asyncgen()")); + } + + // PY-20770 + public void testAsyncGeneratorDunderAiter() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("AsyncIterator[int]", + "async def asyncgen():\n" + + " yield 42\n" + + "expr = asyncgen().__aiter__()")); + } + + // PY-20770 + public void testAsyncGeneratorDunderAnext() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("Awaitable[int]", + "async def asyncgen():\n" + + " yield 42\n" + + "expr = asyncgen().__anext__()")); + } + + // PY-20770 + public void testAsyncGeneratorAwaitOnDunderAnext() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("int", + "async def asyncgen():\n" + + " yield 42\n" + + "async def asyncusage()\n" + + " expr = await asyncgen().__anext__()")); + } + + // PY-20770 + public void testAsyncGeneratorAsend() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("Awaitable[int]", + "async def asyncgen():\n" + + " yield 42\n" + + "expr = asyncgen().asend(\"hello\")")); + } + + // PY-20770 + public void testAsyncGeneratorAwaitOnAsend() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("int", + "async def asyncgen():\n" + + " yield 42\n" + + "async def asyncusage():\n" + + " expr = await asyncgen().asend(\"hello\")")); + } + + // PY-20770 + public void testIteratedAsyncGeneratorElement() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest("int", + "async def asyncgen():\n" + + " yield 10\n" + + "async def run():\n" + + " async for i in asyncgen():\n" + + " expr = i")); + } + private void doTest(final String expectedType, final String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 54575500e451..ab6329e5fbef 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -31,7 +31,7 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { } private void doTest() { - runWithLanguageLevel(LanguageLevel.PYTHON35, () -> { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> { myFixture.copyDirectoryToProject("typing", ""); myFixture.configureByFile(TEST_DIRECTORY + getTestName(false) + ".py"); myFixture.enableInspections(PyTypeCheckerInspection.class); @@ -40,7 +40,7 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { } private void doMultiFileTest() { - runWithLanguageLevel(LanguageLevel.PYTHON35, () -> { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> { myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), ""); myFixture.copyDirectoryToProject("typing", ""); myFixture.configureFromTempProjectFile("a.py"); @@ -133,4 +133,14 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testFunctionReturnTypePy3() { doTest(); } + + // PY-20770 + public void testAsyncForOverAsyncGenerator() { + doTest(); + } + + // PY-20770 + public void testForOverAsyncGenerator() { + doTest(); + } }