diff --git a/python/python-psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java b/python/python-psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java index 3cbf16d0eba3..2842f1f291ae 100644 --- a/python/python-psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java +++ b/python/python-psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java @@ -38,6 +38,8 @@ public class PyCustomMember extends UserDataHolderBase { private final PsiElement myTarget; private PyPsiPath myPsiPath; + private boolean myIsClassVar = false; + boolean myFunction = false; /** @@ -243,6 +245,16 @@ public class PyCustomMember extends UserDataHolderBase { return myFunction; } + + public PyCustomMember asClassVar() { + myIsClassVar = true; + return this; + } + + public boolean isClassVar() { + return myIsClassVar; + } + /** * Checks if some reference points to this element * diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index ce4d584b551a..877fdb5dbd31 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -1102,6 +1102,8 @@ INSP.protocol.newtype.cannot.be.used.with.protocol.classes=NewType cannot be use INSP.protocol.element.type.incompatible.with.protocol=Type of ''{0}'' is incompatible with ''{1}'' INSP.protocol.cannot.instantiate.protocol.class=Cannot instantiate protocol class ''{0}'' INSP.protocol.element.type.not.writable=''{0}'' is writable in protocol ''{1}'' +INSP.protocol.element.type.not.classvar=Cannot override class variable from protocol ''{0}'' with instance variable +INSP.protocol.element.type.not.instancevar=Cannot override instance variable from protocol ''{0}'' with class variable # PyShadowingBuiltinsInspection INSP.NAME.shadowing.builtins=Shadowing built-in names diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyDataclassClassMembersProvider.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyDataclassClassMembersProvider.java index a05d0859c025..17e38ab4e76c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyDataclassClassMembersProvider.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyDataclassClassMembersProvider.java @@ -51,7 +51,7 @@ public final class PyDataclassClassMembersProvider extends PyClassMembersProvide if (hasAttrs) { PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(pyClass); PyClass objectClass = builtinCache.getClass(PyNames.OBJECT); - result.add(new PyCustomMember("__attrs_attrs__", objectClass)); + result.add(new PyCustomMember("__attrs_attrs__", objectClass).asClassVar()); } return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 8fd11e19e2b7..488873c02174 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -17,6 +17,7 @@ import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.codeInsight.PyCustomMemberUtils; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.*; import com.jetbrains.python.psi.impl.references.PyReferenceImpl; @@ -607,7 +608,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { type = resolveContext.getTypeEvalContext().getType(typedElement); } - result.add(new PyTypeMember(element, type)); + boolean isClassVar = false; + if (element instanceof PyAnnotationOwner && element instanceof PyTypeCommentOwner) { + isClassVar = + PyTypingTypeProvider.isClassVar((PyAnnotationOwner & PyTypeCommentOwner)element, resolveContext.getTypeEvalContext()); + } + result.add(new PyTypeMember(element, type, isClassVar)); } } return true; @@ -689,12 +695,16 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return ContainerUtil.map(results, result -> { PsiElement element = result.getElement(); + boolean isClassVar = false; + if (element instanceof PyAnnotationOwner && element instanceof PyTypeCommentOwner) { + isClassVar = PyTypingTypeProvider.isClassVar((PyAnnotationOwner & PyTypeCommentOwner)element, context.getTypeEvalContext()); + } if (element instanceof PyTypedElement typedElement) { return new PyTypeMember(typedElement, - context.getTypeEvalContext().getType(typedElement), typedElement, typedElement, typedElement); + context.getTypeEvalContext().getType(typedElement), isClassVar); } else { - return new PyTypeMember(element, null, element, element, element); + return new PyTypeMember(element, null, isClassVar); } }); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java index d7026eaa7c89..e2e1ecb1a1bf 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -596,6 +596,12 @@ public final class PyTypeChecker { if (protocolMember.isDeletable() && !subclassElementMember.isDeletable()) { return false; } + boolean isProtocolMemberClassVar = protocolMember.isClassVar(); + boolean isSubclassMemberClassVar = subclassElementMember.isClassVar(); + if (isSubclassMemberClassVar != isProtocolMemberClassVar) { + return false; + } + PyType subclassElementType = dropSelfIfNeeded(actual, subclassElementMember.getType(), matchContext.context); subclassElementType = substitute(subclassElementType, substitutions, matchContext.context); boolean matched = match(protocolElementType, subclassElementType, protocolContext).orElse(true); diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index db5e9f6c0a21..4499932bf7fc 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -305,7 +305,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { """ import attr import typing - + @attr.s class Weak1: x = attr.ib() @@ -313,8 +313,8 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { z = attr.ib(default=attr.Factory(list)) \s Weak1(1, "str", 2) - - + + @attr.s class Weak2: x = attr.ib() @@ -324,8 +324,8 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { return 1 \s Weak2("str") - - + + @attr.s class Strong: x = attr.ib(type=int) @@ -421,25 +421,25 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testTypingLiteralStrings() { doTestByText(""" from typing_extensions import Literal - + a: Literal["abc"] = undefined b: Literal[b"abc"] = undefined - + def foo1(p1: Literal["abc"]): pass foo1(a) foo1(b) - + def foo2(p1: Literal[b"abc"]): pass foo2(a) foo2(b) - + def foo3(p1: str): pass foo3(a) foo3(b) - + def foo4(p1: bytes): pass foo4(a) @@ -450,19 +450,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { // PY-46385 public void testAliasingEnumClassNameInLiteralType() { doTestByText(""" - from enum import Enum - from typing import Literal - - class Colors(Enum): - RED = 1 - GREEN = 1 - BLUE = 3 - - AliasColors = Colors - - x: AliasColors = Colors.RED - y: Literal[Colors.RED] = Colors.GREEN - z: Literal[AliasColors.RED] = Colors.RED"""); + from enum import Enum + from typing import Literal + + class Colors(Enum): + RED = 1 + GREEN = 1 + BLUE = 3 + + AliasColors = Colors + + x: AliasColors = Colors.RED + y: Literal[Colors.RED] = Colors.GREEN + z: Literal[AliasColors.RED] = Colors.RED"""); } // PY-46385 @@ -506,7 +506,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { """ from enum import Enum from typing import Literal - + class Color(Enum): R = 1 G = 2 @@ -514,7 +514,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { RED = R GREEN = G BLUE = B - + def foo(v: Color | str) -> None: if v is Color.RED: r: Literal[Color.R] = v @@ -530,7 +530,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { pass else: s: str = v - + def bar(v: Literal[Color.R, "1"]) -> None: if isinstance(v, Color): r: Literal[Color.R] = v @@ -654,11 +654,11 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { """ import pathlib import os - + def foo(p: pathlib.Path): with open(p) as file: pass - + p1: pathlib.Path p2: os.PathLike[bytes] = p1 p3: os.PathLike[str] = p1""" @@ -686,10 +686,10 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParameterizedClassAgainstType() { doTestByText(""" from typing import Type, Any, List - + def my_function(param: Type[Any]): pass - + my_function(List[str])"""); } @@ -697,10 +697,10 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testUnionAgainstType() { doTestByText(""" from typing import Type, Any, Union - + def my_function(param: Type[Any]): pass - + my_function(Union[int, str])"""); } @@ -864,7 +864,8 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { // PY-44974 public void testParenthesizedBitwiseOrUnionOfUnionsAssignNone() { - doTestByText("bar: int | ((list | dict) | (float | str)) = None"); + doTestByText( + "bar: int | ((list | dict) | (float | str)) = None"); } // PY-44974 @@ -885,17 +886,17 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecExample() { doTestByText(""" from typing import Callable, ParamSpec - + P = ParamSpec("P") - - + + def changes_return_type_to_str(x: Callable[P, int]) -> Callable[P, str]: ... - - + + def returns_int(a: str, b: bool) -> int: return 42 - - + + changes_return_type_to_str(returns_int)("42", 42)"""); } @@ -903,23 +904,23 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecUserGenericClassMethod() { doTestByText(""" from typing import TypeVar, Generic, Callable, ParamSpec - + U = TypeVar("U") P = ParamSpec("P") - - + + class Y(Generic[U, P]): f: Callable[P, U] attr: U - + def __init__(self, f: Callable[P, U], attr: U) -> None: self.f = f self.attr = attr - - + + def a(q: int) -> str: ... - - + + expr = Y(a, '1').f("42") """); } @@ -928,23 +929,23 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecUserGenericClassMethodSeveralParameters() { doTestByText(""" from typing import TypeVar, Generic, Callable, ParamSpec - + U = TypeVar("U") P = ParamSpec("P") - - + + class Y(Generic[U, P]): f: Callable[P, U] attr: U - + def __init__(self, f: Callable[P, U], attr: U) -> None: self.f = f self.attr = attr - - + + def a(q: int, s: str) -> str: ... - - + + expr = Y(a, '1').f(42, 42) """); } @@ -953,23 +954,23 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecUserGenericClassMethodConcatenate() { doTestByText(""" from typing import TypeVar, Generic, Callable, ParamSpec, Concatenate - + U = TypeVar("U") P = ParamSpec("P") - - + + class Y(Generic[U, P]): f: Callable[Concatenate[int, P], U] attr: U - + def __init__(self, f: Callable[Concatenate[int, P], U], attr: U) -> None: self.f = f self.attr = attr - - + + def a(q: int, s: str, b: bool) -> str: ... - - + + expr = Y(a, '1').f(42, 42, 42) """); } @@ -978,16 +979,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateAddThirdParameter() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... - - + + add(bar)("42", 42, 42)"""); } @@ -995,16 +996,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateAddSecondParameter() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... - - + + add(bar)("42", "42", True)"""); } @@ -1012,16 +1013,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateAddFirstParameter() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... - - + + add(bar)(42, 42, True)"""); } @@ -1029,16 +1030,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateAddFirstSeveralParameters() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def add(x: Callable[P, int]) -> Callable[Concatenate[str, list[str], P], bool]: ... - - + + add(bar)(42, [42], 3, True)"""); } @@ -1046,16 +1047,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateAddOk() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... - - + + add(bar)("42", 42, True, True, True)"""); } @@ -1063,16 +1064,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateRemove() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... - - + + remove(bar)(42)"""); } @@ -1080,16 +1081,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateRemoveOkOneBool() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... - - + + remove(bar)(True)"""); } @@ -1097,16 +1098,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateRemoveOkTwoBools() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... - - + + remove(bar)(True, True)"""); } @@ -1114,16 +1115,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateRemoveOkEmpty() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... - - + + remove(bar)()"""); } @@ -1131,21 +1132,21 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParamSpecConcatenateTransform() { doTestByText(""" from typing import Callable, Concatenate, ParamSpec - + P = ParamSpec("P") - - + + def bar(x: int, *args: bool) -> int: ... - - + + def transform( x: Callable[Concatenate[int, P], int] ) -> Callable[Concatenate[str, P], bool]: def inner(s: str, *args: P.args): return True return inner - - + + transform(bar)(42)"""); } @@ -1202,13 +1203,13 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testBitwiseOrUnionWithNotCalculatedGenericFromUnion() { doTestByText(""" from typing import Union, TypeVar - + T = TypeVar("T", bytes, str) - + my_union = Union[str, set[T]] another_union = Union[list[str], my_union[T]] - - + + def foo(path_or_buf: another_union[T] | None) -> None: print(path_or_buf) """); @@ -1218,24 +1219,24 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testFunctionNamedParameterUnification() { doTestByText(""" from typing import Callable, ParamSpec - + P = ParamSpec("P") - - + + def twice(f: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> int: return f(*args, **kwargs) + f(*args, **kwargs) - - + + def a_int_b_str(a: int, b: str) -> int: return a + len(b) - - + + res1 = twice(a_int_b_str, 1, "A") - + res2 = twice(a_int_b_str, b="A", a=1) - + res3 = twice(a_int_b_str, "A", 1) - + res4 = twice(a_int_b_str, b=1, a="A")"""); } @@ -1243,19 +1244,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testFunctionNotEnoughArgumentsToMatchWithParamSpec() { doTestByText(""" from typing import ParamSpec, Callable, TypeVar - + P = ParamSpec('P') T = TypeVar('T') - - + + def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs): f(*args, **kwargs) - - + + def func(n: int, s: str) -> None: pass - - + + caller(func, 42) """); } @@ -1264,19 +1265,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testFunctionTooManyArgumentsToMatchWithParamSpec() { doTestByText(""" from typing import ParamSpec, Callable, TypeVar - + P = ParamSpec('P') T = TypeVar('T') - - + + def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs): f(*args, **kwargs) - - + + def func(n: int, s: str) -> None: pass - - + + caller(func, 42, 'foo', None)"""); } @@ -1284,19 +1285,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testFunctionNamedArgumentNotMatchWithParamSpec() { doTestByText(""" from typing import ParamSpec, Callable, TypeVar - + P = ParamSpec('P') T = TypeVar('T') - - + + def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs): f(*args, **kwargs) - - + + def func(foo: int) -> None: pass - - + + caller(func, bar=42)"""); } @@ -1304,81 +1305,81 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testSameArgumentPassedTwiceInParamSpec() { doTestByText(""" from typing import ParamSpec, Callable, TypeVar - + P = ParamSpec('P') T = TypeVar('T') - - + + def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs): f(*args, **kwargs) - - + + def func(n: int) -> None: pass - - + + caller(func, 42, n=42)"""); } // PY-80704 public void testParamSpecDerived() { doTestByText(""" - class Base[**P]: ... - b: Base[[int]] - - class Derived1(Base[int]): ... - b = Derived1() - - class Derived2(Base[str]): ... - b = Derived2() - - class Derived3[**P](Base[P]): ... - b = Derived3() - """); + class Base[**P]: ... + b: Base[[int]] + + class Derived1(Base[int]): ... + b = Derived1() + + class Derived2(Base[str]): ... + b = Derived2() + + class Derived3[**P](Base[P]): ... + b = Derived3() + """); } // PY-80704 public void testParamSpecProtocol() { doTestByText(""" - from typing import Protocol, Callable - - class Proto[**P](Protocol): - f: Callable[P, None] - p: Proto[[int]] - - class Match: - f: Callable[[int], None] - p = Match() - - class Mismatch: - f: Callable[[str], None] - p = Mismatch() - """); + from typing import Protocol, Callable + + class Proto[**P](Protocol): + f: Callable[P, None] + p: Proto[[int]] + + class Match: + f: Callable[[int], None] + p = Match() + + class Mismatch: + f: Callable[[str], None] + p = Mismatch() + """); } public void testParamSpecProtocolEmpty() { doTestByText(""" - from typing import Protocol - - class Proto[**P](Protocol): ... - - _: Proto[[]] = 1 - """); + from typing import Protocol + + class Proto[**P](Protocol): ... + + _: Proto[[]] = 1 + """); } // PY-80775 public void testParamSpecProtocolFull() { doTestByText(""" - from typing import Protocol - - class Proto[**P](Protocol): - def f(self, *args: P.args, **kwargs: P.kwargs) -> None: ... - - class Impl: - def f(self, i: int) -> None: ... - - p: Proto[[int]] = Impl() - """); + from typing import Protocol + + class Proto[**P](Protocol): + def f(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + + class Impl: + def f(self, i: int) -> None: ... + + p: Proto[[int]] = Impl() + """); } // PY-46661 @@ -1563,33 +1564,33 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParameterSelf() { doTestByText(""" from typing import Self, Callable - + class Shape: def difference(self, other: Self) -> float: ... - + def apply(self, f: Callable[[Self], None]) -> None: ... - - + + class Circle(Shape): pass - - + + def fCircle(c: Circle): pass - - + + def fShape(sh: Shape): pass - - + + sh = Shape() cir = Circle() - + sh.difference(cir) sh.difference(sh) cir.difference(cir) cir.difference(sh) - + cir.apply(fCircle) cir.apply(fShape) sh.apply(fCircle) @@ -1600,23 +1601,23 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParameterTypeSelf() { doTestByText(""" from typing import Self, Callable - + class MyClass: def foo(self, bar: Type[Self]) -> None: ... - - + + class SubClass(MyClass): pass - - + + myClass = MyClass() subClass = MySubClass() - + myClass.foo(myClass) myClass.foo(subClass) myClass.foo(MyClass) myClass.foo(SubClass) - + subClass.foo(myClass) subClass.foo(subClass) subClass.foo(MyClass) @@ -1627,24 +1628,24 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParameterTypeSelfUnion() { doTestByText(""" from typing import Self, Callable - + class MyClass: def foo(self, bar: Self | None | int) -> None: ... - - + + class SubClass(MyClass): pass - - + + myClass = MyClass() subClass = SubClass() - + myClass.foo(myClass) myClass.foo(subClass) myClass.foo(42) myClass.foo(None) myClass.foo("") - + subClass.foo(myClass) subClass.foo(subClass) subClass.foo(42) @@ -1656,23 +1657,23 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testParameterTypeSelfReturnAsParameter() { doTestByText(""" from typing import Self, Callable - + class MyClass: def foo(self, bar: Self) -> Self: ... - - + + class SubClass(MyClass): pass - - + + myClass = MyClass() subClass = SubClass() - + myClass.foo(myClass.foo(myClass)) myClass.foo(subClass.foo(subClass)) myClass.foo(myClass.foo(subClass)) myClass.foo(subClass.foo(myClass)) - + subClass.foo(myClass.foo(myClass)) subClass.foo(subClass.foo(subClass)) subClass.foo(myClass.foo(subClass)) @@ -1684,21 +1685,21 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self, Protocol - - + + class MyProtocol(Protocol): def foo(self, bar: float) -> Self: ... - - + + class MyClass: def foo(self, bar: float) -> MyClass: pass - - + + def accepts_protocol(obj: MyProtocol) -> None: print(obj) - - + + obj = MyClass() accepts_protocol(obj) """); @@ -1709,25 +1710,25 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self, Protocol - - + + class MyProtocol(Protocol): def foo(self, bar: float) -> Self: ... - - + + class MyClass: def foo(self, bar: float) -> MySubClass: pass - - + + class MySubClass(MyClass): pass - - + + def accepts_protocol(obj: MyProtocol) -> None: print(obj) - - + + obj = MyClass() accepts_protocol(obj) """); @@ -1738,21 +1739,21 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self, Protocol - - + + class MyProtocol(Protocol): def foo(self, bar: float) -> Self: ... - - + + class MyClass: def foo(self, bar: float) -> int: pass - - + + def accepts_protocol(obj: MyProtocol) -> None: print(obj) - - + + obj = MyClass() accepts_protocol(obj) """); @@ -1763,26 +1764,26 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self, Protocol - - + + class MyProtocol(Protocol): def foo(self, bar: float) -> Self: ... - - + + class MyClass: def foo(self, bar: float) -> MyClassNotSubclass: pass - - + + class MyClassNotSubclass: def foo(self, bar: float) -> int: pass - - + + def accepts_protocol(obj: MyProtocol) -> None: print(obj) - - + + obj = MyClass() accepts_protocol(obj) """); @@ -1793,21 +1794,21 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self, Protocol - - + + class MyProtocol(Protocol): def foo(self, bar: float) -> Self: ... - - + + class MyClass: def foo(self, bar: float) -> Self: pass - - + + def accepts_protocol(obj: MyProtocol) -> None: print(obj) - - + + obj = MyClass() accepts_protocol(obj) """); @@ -1817,15 +1818,15 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericInFunction() { doTestByText(""" from typing import Tuple, TypeVarTuple, TypeVar - + T = TypeVar('T') Ts = TypeVarTuple('Ts') - - + + def foo(x: T, y: Tuple[*Ts]): pass - - + + foo(10, (1, '1', [1])) """); } @@ -1834,16 +1835,16 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericArgumentByCallableInFunction() { doTestByText(""" from typing import Callable, TypeVarTuple, Tuple - + Ts = TypeVarTuple('Ts') - - + + def foo(a: int, f: Callable[[*Ts], None], args: Tuple[*Ts]) -> None: ... def bar(a: int, b: str) -> None: ... - - + + foo(1, bar, args=(0, 'foo')) - + foo(1, bar, args=('foo', 0)) """); } @@ -1852,26 +1853,26 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericCheckCallableInFunction() { doTestByText(""" from typing import TypeVar, TypeVarTuple, Callable, Tuple - + T = TypeVar('T') Ts = TypeVarTuple('Ts') - - + + def foo(f: Callable[[int, *Ts, T], Tuple[T, *Ts]]) -> None: ... - - + + def ok1(a: int, b: str, c: bool, d: list[int]) -> Tuple[list[int], str, bool]: ... def ok2(a: int, b: str) -> Tuple[str]: ... - - + + foo(ok1) foo(ok2) - - + + def err1(a: int, b: str, c: bool, d: list[int]) -> Tuple[list[int], str, str]: ... def err2(a: int, b: str) -> Tuple[str, str]: ... - - + + foo(err1) foo(err2) """); @@ -1881,24 +1882,24 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericTwoTsInFunction() { doTestByText(""" from typing import TypeVarTuple, Generic - + Ts = TypeVarTuple('Ts') - - + + class Array(Generic[*Ts]): ... - - + + def foo(x: Array[*Ts], y: Array[*Ts]) -> Array[*Ts]: ... - - + + x: Array[int] y: Array[str] z: Array[int, str] - + foo(x, x) - + foo(x, y) foo(x, z) """); @@ -1908,31 +1909,31 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericUnboundTupleInFunction() { doTestByText(""" from typing import Generic, TypeVarTuple, Tuple, Any - + Ts = TypeVarTuple('Ts') - - + + class Array(Generic[*Ts]): def __init__(self, shape: Tuple[*Ts]): ... - - + + def foo(x: Array[int, *Tuple[Any, ...], str]) -> None: ... - - + + x: Array[int, list[str], bool, str] foo(x) - + y: Array[int, str] foo(y) - + z: Array[int] foo(z) - + t: Array[str] foo(t) - + k: Array[int, int] foo(k) """); @@ -1942,17 +1943,17 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsNamedParameters() { doTestByText(""" from typing import Tuple, TypeVarTuple - + Ts = TypeVarTuple('Ts') - - + + def foo(a: str, *args: *Tuple[*Ts, int], b: str, c: bool) -> None: ... - - + + foo('', 1, True, [1], 42, b='', c=True) foo('', 42, b='', c=True) foo('', True, 42, c=True, b='') - + foo('', b='', c=True) foo('', '', b='', c=True) foo('', '', [False], b='', c=True) @@ -1964,19 +1965,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsTupleAndUnpackedTuple() { doTestByText(""" from typing import Tuple, TypeVarTuple - + Ts = TypeVarTuple('Ts') - - + + def foo(a: Tuple[*Ts], *args: *Tuple[str, *Ts, int], b: str) -> None: ... - - + + foo(('', 1), '', '', 1, 1, b='') foo((1,1), '', 1, 1, 1, b='') foo(('',), '', '', 1, b='') foo((), '', 1, b='') foo(([], {}), '', [], {}, 1, b='') - + foo(('', 1), b='') foo(('', 1), '', '', '', 1, b='') foo((1,1), '', 1, 1, b='') @@ -1991,11 +1992,11 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsOfVariadicGeneric() { doTestByText(""" from typing import Tuple, TypeVarTuple - + Ts = TypeVarTuple('Ts') - + def foo(*args: Tuple[*Ts]): ... - + foo((0,), (1,)) foo((0,), (1, 2)) # Should fail according to https://typing.python.org/en/latest/spec/generics.html#type-variable-tuple-equality @@ -2007,19 +2008,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsOfVariadicGenericPrefixSuffix() { doTestByText(""" from typing import Tuple, TypeVarTuple - + Ts = TypeVarTuple('Ts') - - + + def foo(a: Tuple[*Ts], *args: *Tuple[str, *Ts, int], b: str) -> None: ... - - + + foo(('', 1), '', '', 1, 1, b='') foo((1,1), '', 1, 1, 1, b='') foo(('',), '', '', 1, b='') foo((), '', 1, b='') foo(([], {}), '', [], {}, 1, b='') - + foo(('', 1), b='') foo(('', 1), '', '', '', 1, b='') foo((1,1), '', 1, 1, b='') @@ -2034,19 +2035,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsPrefixSuffix() { doTestByText(""" from typing import Tuple, TypeVarTuple - + Ts = TypeVarTuple('Ts') - - + + def foo(a: Tuple[*Ts], *args: *Tuple[str, *Ts, int], b: str) -> None: ... - - + + foo(('', 1), '', '', 1, 1, b='') foo((1,1), '', 1, 1, 1, b='') foo(('',), '', '', 1, b='') foo((), '', 1, b='') foo(([], {}), '', [], {}, 1, b='') - + foo(('', 1), b='') foo(('', 1), '', '', '', 1, b='') foo((1,1), '', 1, 1, b='') @@ -2061,15 +2062,15 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testVariadicGenericStarArgsUnboundTuple() { doTestByText(""" from typing import Tuple - - + + def foo(*args: *Tuple[int, ...]) -> None: ... - - + + foo() foo(1) foo(1, 2, 3) - + foo('') foo(1, '') """); @@ -2078,47 +2079,47 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { // PY-53105 public void testVariadicGenericMatchWithHomogeneousGenericVariadic() { doTestByText(""" - from __future__ import annotations - - from typing import TypeVarTuple - from typing import Generic - from typing import Any - - Shape = TypeVarTuple("Shape") - - class Array(Generic[*Shape]): - ... - - y: Array[*tuple[Any, ...]] = Array() - - def expect_variadic_array(x: Array[int, *Shape]) -> None: - print(x) - - expect_variadic_array(y) - """); + from __future__ import annotations + + from typing import TypeVarTuple + from typing import Generic + from typing import Any + + Shape = TypeVarTuple("Shape") + + class Array(Generic[*Shape]): + ... + + y: Array[*tuple[Any, ...]] = Array() + + def expect_variadic_array(x: Array[int, *Shape]) -> None: + print(x) + + expect_variadic_array(y) + """); } // PY-53105 public void testVariadicGenericMatchWithHomogeneousGenericVariadicAndOtherTypes() { doTestByText(""" - from __future__ import annotations - - from typing import TypeVarTuple - from typing import Generic - from typing import Any - - Shape = TypeVarTuple("Shape") - - class Array(Generic[*Shape]): - ... - - y: Array[*tuple[Any, ...], int, str] = Array() - - def expect_variadic_array(x: Array[int, *Shape]) -> None: - print(x) - - expect_variadic_array(y) - """); + from __future__ import annotations + + from typing import TypeVarTuple + from typing import Generic + from typing import Any + + Shape = TypeVarTuple("Shape") + + class Array(Generic[*Shape]): + ... + + y: Array[*tuple[Any, ...], int, str] = Array() + + def expect_variadic_array(x: Array[int, *Shape]) -> None: + print(x) + + expect_variadic_array(y) + """); } // PY-53105 @@ -2147,7 +2148,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { y: Float32Array[Height] = Array() takes_float_array_of_specific_shape(y) - """); + """); } // PY-53105 @@ -2176,33 +2177,33 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { y: Float32Array[Height, Width] = Array() takes_float_array_of_specific_shape(y) - """); + """); } // PY-53105 public void testVariadicGenericEmpty() { doTestByText(""" from typing import TypeVarTuple - + Ts = TypeVarTuple("Ts") - + IntTuple = tuple[int, *Ts] - + c: IntTuple[()] = (1, "") - """); + """); } // PY-56785 public void testTypingSelfNoInspectionReturnSelfMethod() { doTestByText(""" from typing import Self - - + + class Builder: def foo(self) -> Self: result = self.bar() return result - + def bar(self) -> Self: pass """); @@ -2212,12 +2213,12 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void testTypingSelfClassMethodReturnClsNoHighlighting() { doTestByText(""" from typing import Self - + class Shape: - + def __init__(self, scale: float): self.scale = None - + @classmethod def from_config(cls, config: dict[str, float]) -> Self: return cls(config["scale"]) @@ -2229,7 +2230,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from __future__ import annotations from typing import Self - + class SomeClass(): def foo(self, bar: Self) -> Self: return SomeClass() @@ -2240,17 +2241,17 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { public void _testTypingSelfReturnSubClassMethod() { doTestByText(""" from typing import Self - - + + class Builder: def foo(self) -> Self: result = SubBuilder().bar() return result - + def bar(self) -> Self: pass - - + + class SubBuilder(Builder): pass """); @@ -2320,14 +2321,14 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { doTestByText(""" from typing_extensions import LiteralString def expect_literal_string(s: LiteralString) -> None: ... - + expect_literal_string("foo" + "bar") literal_string: LiteralString expect_literal_string(literal_string + "bar") - + literal_string2: LiteralString expect_literal_string(literal_string + literal_string2) - + plain_string: str expect_literal_string(literal_string + plain_string) expect_literal_string(plain_string + literal_string) @@ -2345,7 +2346,7 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { expect_literal_string(literal_string.join(["foo", "bar"])) literal_string2: LiteralString expect_literal_string(literal_string.join([literal_string, literal_string2])) - + xs: List[LiteralString] expect_literal_string(literal_string.join(xs)) plain_string: str @@ -2463,10 +2464,10 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { runWithLanguageLevel( LanguageLevel.getLatest(), () -> doTestByText(""" -from typing import TypeGuard -def foo(param: str | int) -> TypeGuard[str]: - return param - """) + from typing import TypeGuard + def foo(param: str | int) -> TypeGuard[str]: + return param + """) ); } @@ -2503,36 +2504,36 @@ def foo(param: str | int) -> TypeGuard[str]: // PY-70528 public void testVersionDependentTypeVarTupleInitialization() { doTestByText(""" - import sys - - if sys.version_info >= (3, 11): - from typing import TypeVarTuple - else: - from typing_extensions import TypeVarTuple - - PosArgsT = TypeVarTuple("PosArgsT") - """); + import sys + + if sys.version_info >= (3, 11): + from typing import TypeVarTuple + else: + from typing_extensions import TypeVarTuple + + PosArgsT = TypeVarTuple("PosArgsT") + """); } // PY-23067 public void testFunctoolsWraps() { doTestByText(""" import functools - + class MyClass: def foo(self, i: int): pass - + class Route: @functools.wraps(MyClass.foo) def __init__(self): pass - + class Router: @functools.wraps(wrapped=Route.__init__) def route(self, s: str): pass - + router = Router() router.route(-2) router.route("") @@ -2740,7 +2741,7 @@ def foo(param: str | int) -> TypeGuard[str]: doTestByText(""" class A[T]: def __init__(self, v: T) -> None: ... - + A[int]("") """); } @@ -2751,12 +2752,12 @@ def foo(param: str | int) -> TypeGuard[str]: class Node[T]: x: T - + Node[int].x = 1 Node[int].x Node.x = 1 Node.x - + p = Node[int]() type(p).x i: int = p.x @@ -2888,7 +2889,7 @@ def foo(param: str | int) -> TypeGuard[str]: class NT(NamedTuple): a: str b: int - + x: tuple[str, int] = NT("a", 1) y: tuple[str, int, str] = NT("a", 1) """); @@ -3072,10 +3073,10 @@ def foo(param: str | int) -> TypeGuard[str]: def int_bool(x: int, y: bool) -> str: pass - + def single_str(x: str) -> str: pass - + def empty() -> str: pass @@ -3216,6 +3217,54 @@ def foo(param: str | int) -> TypeGuard[str]: """); } + // PY-76822 + public void testProtocolClassVarAndSubclassClassVar() { + doTestByText(""" + from typing import Protocol, ClassVar + + class Template(Protocol): + val: ClassVar[int] = 0 + + + class Concrete: + val: ClassVar[int] = 0 + + var: Template = Concrete() + """); + } + + // PY-76822 + public void testProtocolClassVarAndSubclassInstanceVar() { + doTestByText(""" + from typing import Protocol, ClassVar + + class Template(Protocol): + val: ClassVar[int] = 0 + + + class Concrete: + val: int = 0 + + var: Template = Concrete() + """); + } + + // PY-76822 + public void testProtocolInstanceVarAndSubclassClassVar() { + doTestByText(""" + from typing import Protocol, ClassVar + + class Template(Protocol): + val: int = 0 + + + class Concrete: + val: ClassVar[int] = 0 + + var: Template = Concrete() + """); + } + // PY-76822 public void testProtocolWithPropertyDeleterAndFrozenDataclass() { doTestByText("""