Update conformance test suite

GitOrigin-RevId: da76f385342a1492bcd536750eea4f7ecc86a715
This commit is contained in:
evgeny.bovykin
2025-04-23 23:43:33 +00:00
committed by intellij-monorepo-bot
parent 16b9128c55
commit e77f3a1125
45 changed files with 1403 additions and 116 deletions
@@ -0,0 +1,51 @@
"""
Support module for directive_deprecated.
"""
from typing import Self, overload
from typing_extensions import deprecated
@deprecated("Use Spam instead")
class Ham:
...
@deprecated("It is pining for the fjords")
def norwegian_blue(x: int) -> int:
...
@overload
@deprecated("Only str will be allowed")
def foo(x: int) -> str:
...
@overload
def foo(x: str) -> str:
...
def foo(x: int | str) -> str:
...
class Spam:
@deprecated("There is enough spam in the world")
def __add__(self, other: object) -> Self:
...
@property
@deprecated("All spam will be equally greasy")
def greasy(self) -> float:
...
@property
def shape(self) -> str:
...
@shape.setter
@deprecated("Shapes are becoming immutable")
def shape(self, value: str) -> None:
...
@@ -3,7 +3,6 @@ aliases_recursive.py
aliases_type_statement.py
aliases_typealiastype.py
aliases_variance.py
annotations_coroutines.py
annotations_forward_refs.py
annotations_generators.py
annotations_typeexpr.py
@@ -20,12 +19,16 @@ dataclasses_hash.py
dataclasses_inheritance.py
dataclasses_order.py
dataclasses_slots.py
dataclasses_transform_converter.py
dataclasses_transform_field.py
dataclasses_transform_meta.py
dataclasses_usage.py
directives_deprecated.py
directives_type_checking.py
directives_version_platform.py
enums_members.py
generics_base_class.py
generics_basic.py
generics_defaults.py
generics_defaults_referential.py
generics_paramspec_components.py
@@ -49,6 +52,10 @@ namedtuples_type_compat.py
namedtuples_usage.py
narrowing_typeis.py
overloads_basic.py
overloads_consistency.py
overloads_definitions.py
overloads_definitions_stub.pyi
overloads_evaluation.py
protocols_class_objects.py
protocols_definition.py
protocols_explicit.py
@@ -0,0 +1,7 @@
"""
Used as part of the test for the typing.Final special form.
"""
from typing import Final
TEN: Final[int] = 10
@@ -0,0 +1,7 @@
"""
Used as part of the test for the typing.Final special form.
"""
from typing import Final
PI: Final = 3.14
@@ -48,10 +48,8 @@ type BadTypeAlias11 = 1 # E
type BadTypeAlias12 = list or set # E
type BadTypeAlias13 = f"{'int'}" # E
if 1 < 2:
type BadTypeAlias14 = int # E: redeclared
else:
type BadTypeAlias14 = int
type BadTypeAlias14 = int # E[TA14]: redeclared
type BadTypeAlias14 = int # E[TA14]: redeclared
def func3():
@@ -87,5 +85,5 @@ type RecursiveTypeAlias4[T] = T | RecursiveTypeAlias4[str] # E: circular definit
type RecursiveTypeAlias5[T] = T | list[RecursiveTypeAlias5[T]]
type RecursiveTypeAlias6 = RecursiveTypeAlias7 # E: circular definition
type RecursiveTypeAlias7 = RecursiveTypeAlias6
type RecursiveTypeAlias6 = RecursiveTypeAlias7 # E[RTA6+]: circular definition
type RecursiveTypeAlias7 = RecursiveTypeAlias6 # E[RTA6+]: circular definition
@@ -16,7 +16,10 @@ async def func1(ignored: int, /) -> str:
return "spam"
assert_type(func1, Callable[[int], Coroutine[Any, Any, str]])
# Don't use assert_type here because some type checkers infer
# the narrower type types.CoroutineType rather than typing.Coroutine
# in this case.
v1: Callable[[int], Coroutine[Any, Any, str]] = func1
async def func2() -> None:
@@ -38,7 +38,7 @@ var1 = 1
# The following should all generate errors because they are not legal type
# expressions, despite being enclosed in quotes.
def invalid_annotations(
p1: "eval(" ".join(map(chr, [105, 110, 116])))", # E
p1: "eval(''.join(map(chr, [105, 110, 116])))", # E
p2: "[int, str]", # E
p3: "(int, str)", # E
p4: "[int for i in range(1)]", # E
@@ -88,6 +88,9 @@ class ClassD:
y: int = 0 # E: Refers to local int, which isn't a legal type expression
def __init__(self) -> None:
self.ClassC = ClassC()
assert_type(ClassD.str, str)
assert_type(ClassD.x, int)
@@ -179,7 +179,10 @@ async def generator29() -> AsyncIterator[int]:
raise NotImplementedError
assert_type(generator29, Callable[[], Coroutine[Any, Any, AsyncIterator[int]]])
# Don't use assert_type here because some type checkers infer
# the narrower type types.CoroutineType rather than typing.Coroutine
# in this case.
v1: Callable[[], Coroutine[Any, Any, AsyncIterator[int]]] = generator29
async def generator30() -> AsyncIterator[int]:
@@ -121,3 +121,14 @@ T = TypeVar("T", bound=TD2)
def func6(**kwargs: Unpack[T]) -> None: # E: unpacked value must be a TypedDict, not a TypeVar bound to TypedDict.
...
# > The situation where the destination callable contains **kwargs: Unpack[TypedDict] and
# > the source callable doesn’t contain **kwargs should be disallowed. This is because,
# > we cannot be sure that additional keyword arguments are not being passed in when an instance of a subclass
# > had been assigned to a variable with a base class type and then unpacked in the destination callable invocation
def func7(*, v1: int, v3: str, v2: str = "") -> None:
...
v7: TDProtocol6 = func7 # E: source does not have kwargs
@@ -57,8 +57,14 @@ class ClassA(Generic[T, P]):
good1: CV[int] = 1
good2: ClassVar[list[str]] = []
good3: ClassVar[Any] = 1
# > If an assigned value is available, the type should be inferred as some type
# > to which this value is assignable.
# Here, type checkers could infer good4 as `float` or `Any`, for example.
good4: ClassVar = 3.1
good5: Annotated[ClassVar[list[int]], ""] = []
# > If the `ClassVar` qualifier is used without any assigned value, the type
# > should be inferred as `Any`:
good5: ClassVar #E? Type checkers may error on uninitialized ClassVar
good6: Annotated[ClassVar[list[int]], ""] = []
def method1(self, a: ClassVar[int]): # E: ClassVar not allowed here
x: ClassVar[str] = "" # E: ClassVar not allowed here
@@ -75,7 +81,7 @@ bad12: TypeAlias = ClassVar[str] # E: ClassVar not allowed here
assert_type(ClassA.good1, int)
assert_type(ClassA.good2, list[str])
assert_type(ClassA.good3, Any)
assert_type(ClassA.good4, float)
assert_type(ClassA.good5, Any)
class BasicStarship:
@@ -83,6 +89,9 @@ class BasicStarship:
damage: int # Instance variable without default
stats: ClassVar[dict[str, int]] = {} # Class variable
def __init__(self, damage: int) -> None:
self.damage = damage
class Starship:
captain: str = "Picard"
@@ -49,8 +49,8 @@ class ChildA(ParentA):
def method2(self, x: int | str) -> int | str: # OK
return 0
@override
def method3(self) -> int: # E: no matching signature in ancestor
@override # E[method3]
def method3(self) -> int: # E[method3]: no matching signature in ancestor
return 1
@overload # E[method4]
@@ -61,7 +61,7 @@ class ChildA(ParentA):
def method4(self, x: str) -> str:
...
@override
@override # E[method4]
def method4(self, x: int | str) -> int | str: # E[method4]: no matching signature in ancestor
return 0
@@ -75,18 +75,18 @@ class ChildA(ParentA):
# > only normal methods but also @property, @staticmethod, and @classmethod.
@staticmethod
@override
def static_method1() -> int: # E: no matching signature in ancestor
@override # E[static_method1]
def static_method1() -> int: # E[static_method1]: no matching signature in ancestor
return 1
@classmethod
@override
def class_method1(cls) -> int: # E: no matching signature in ancestor
@override # E[class_method1]
def class_method1(cls) -> int: # E[class_method1]: no matching signature in ancestor
return 1
@property
@override
def property1(self) -> int: # E: no matching signature in ancestor
@override # E[property1]
def property1(self) -> int: # E[property1]: no matching signature in ancestor
return 1
@@ -114,7 +114,7 @@ class Class6:
"""__new__ that causes __init__ to be ignored"""
def __new__(cls) -> Class6Proxy:
return Class6Proxy.__new__(cls)
return Class6Proxy()
def __init__(self, x: int) -> None:
"""This __init__ is ignored for purposes of conversion"""
@@ -36,6 +36,9 @@ class ModelBase:
) -> None:
...
def __init__(self, not_a_field: str) -> None:
self.not_a_field = not_a_field
class Customer1(ModelBase, frozen=True):
id: int = model_field()
@@ -0,0 +1,133 @@
"""
Tests the dataclass_transform mechanism supports the "converter" parameter
in a field specifier class.
"""
# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#converters
from typing import (
Any,
Callable,
TypeVar,
dataclass_transform,
overload,
)
T = TypeVar("T")
S = TypeVar("S")
def model_field(
*,
converter: Callable[[S], T],
default: S | None = None,
default_factory: Callable[[], S] | None = None,
) -> T:
...
@dataclass_transform(field_specifiers=(model_field,))
class ModelBase:
...
# > The converter must be a callable that must accept a single positional
# > argument (but may accept other optional arguments, which are ignored for
# > typing purposes).
def bad_converter1() -> int:
return 0
def bad_converter2(*, x: int) -> int:
return 0
class DC1(ModelBase):
field1: int = model_field(converter=bad_converter1) # E
field2: int = model_field(converter=bad_converter2) # E
# > The type of the first positional parameter provides the type of the
# > synthesized __init__ parameter for the field.
# > The return type of the callable must be assignable to the field’s
# > declared type.
def converter_simple(s: str) -> int:
return int(s)
def converter_with_param_before_args(s: str, *args: int, **kwargs: int) -> int:
return int(s)
def converter_with_args(*args: str) -> int:
return int(args[0])
@overload
def overloaded_converter(s: str) -> int:
...
@overload
def overloaded_converter(s: list[str]) -> int:
...
def overloaded_converter(s: str | list[str], *args: str) -> int | str:
return 0
class ConverterClass:
@overload
def __init__(self, val: str) -> None:
...
@overload
def __init__(self, val: bytes) -> None:
...
def __init__(self, val: str | bytes) -> None:
pass
class DC2(ModelBase):
field0: int = model_field(converter=converter_simple)
field1: int = model_field(converter=converter_with_param_before_args)
field2: int = model_field(converter=converter_with_args)
field3: ConverterClass = model_field(converter=ConverterClass)
field4: int = model_field(converter=overloaded_converter)
field5: dict[str, str] = model_field(converter=dict, default=())
DC2(1, "f1", "f2", b"f3", []) # E
DC2("f0", "f1", "f2", 1, []) # E
DC2("f0", "f1", "f2", "f3", 3j) # E
dc1 = DC2("f0", "f1", "f2", b"f6", [])
dc1.field0 = "f1"
dc1.field3 = "f6"
dc1.field3 = b"f6"
dc1.field0 = 1 # E
dc1.field3 = 1 # E
dc2 = DC2("f0", "f1", "f2", "f6", "1", (("a", "1"), ("b", "2")))
# > If default or default_factory are provided, the type of the default value
# > should be assignable to the first positional parameter of the converter.
class DC3(ModelBase):
field0: int = model_field(converter=converter_simple, default="")
field1: int = model_field(converter=converter_simple, default=1) # E
field2: int = model_field(converter=converter_simple, default_factory=str)
field3: int = model_field(converter=converter_simple, default_factory=int) # E
@@ -25,6 +25,9 @@ def model_field(
class ModelMeta(type):
not_a_field: str
def __init__(self, not_a_field: str) -> None:
self.not_a_field = not_a_field
class ModelBase(metaclass=ModelMeta):
def __init_subclass__(
@@ -117,6 +117,7 @@ class DC8(DC7):
def __init__(self, a: DC7, y: int):
self.__dict__ = a.__dict__
self.y = y
a = DC7(3)
@@ -173,7 +174,7 @@ class DC13:
x_squared: int
# This should generate an error because there is no
# This should generate an error because there is no matching
# override __init__ method and no synthesized __init__.
DC13(3) # E
@@ -0,0 +1,120 @@
"""
Tests the warnings.deprecated function.
"""
# pyright: reportDeprecated=true
# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#deprecated
# See also https://peps.python.org/pep-0702/
from typing import Protocol, override
from typing_extensions import deprecated
# > Type checkers should produce a diagnostic whenever they encounter a usage of an object
# > marked as deprecated. [...] For deprecated classes and functions, this includes:
# > * `from` imports
from _directives_deprecated_library import Ham # E: Use of deprecated class Ham
import _directives_deprecated_library as library
# > * References through module, class, or instance attributes
library.norwegian_blue(1) # E: Use of deprecated function norwegian_blue
map(library.norwegian_blue, [1, 2, 3]) # E: Use of deprecated function norwegian_blue
# > For deprecated overloads, this includes all calls that resolve to the deprecated overload.
library.foo(1) # E: Use of deprecated overload for foo
library.foo("x") # OK
ham = Ham() # E?: OK (already reported above)
# > * Any syntax that indirectly triggers a call to the function.
spam = library.Spam()
_ = spam + 1 # E: Use of deprecated method Spam.__add__
spam += 1 # E: Use of deprecated method Spam.__add__
spam.greasy # E: Use of deprecated property Spam.greasy
spam.shape # OK
spam.shape = "cube" # E: Use of deprecated property setter Spam.shape
spam.shape += "cube" # E: Use of deprecated property setter Spam.shape
class Invocable:
@deprecated("Deprecated")
def __call__(self) -> None:
...
invocable = Invocable()
invocable() # E: Use of deprecated method __call__
# > * Any usage of deprecated objects in their defining module
@deprecated("Deprecated")
def lorem() -> None:
...
lorem() # E: Use of deprecated function lorem
# > There are additional scenarios where deprecations could come into play.
# > For example, an object may implement a `typing.Protocol`,
# > but one of the methods required for protocol compliance is deprecated.
# > As scenarios such as this one appear complex and relatively unlikely to come up in practice,
# > this PEP does not mandate that type checkers detect them.
class SupportsFoo1(Protocol):
@deprecated("Deprecated")
def foo(self) -> None:
...
def bar(self) -> None:
...
class FooConcrete1(SupportsFoo1):
@override
def foo(self) -> None: # E?: Implementation of deprecated method foo
...
def bar(self) -> None:
...
def foo_it(f: SupportsFoo1) -> None:
f.foo() # E: Use of deprecated method foo
f.bar()
class SupportsFoo2(Protocol):
def foo(self) -> None:
...
class FooConcrete2:
@deprecated("Deprecated")
def foo(self) -> None:
...
def takes_foo(f: SupportsFoo2) -> None:
...
def caller(c: FooConcrete2) -> None:
takes_foo(
c
) # E?: FooConcrete2 is a SupportsFoo2, but only because of a deprecated method
@@ -5,7 +5,7 @@ Tests that the type checker can distinguish enum members from non-members.
# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members
from enum import Enum, member, nonmember
from typing import Literal, assert_type, reveal_type
from typing import Literal, assert_type
# > If an attribute is defined in the class body with a type annotation but
# > with no assigned value, a type checker should assume this is a non-member
@@ -19,6 +19,10 @@ class Pet(Enum): # E?: Uninitialized attributes (pyre)
CAT = 1 # Member attribute
DOG = 2 # Member attribute
def __init__(self, genus: str, species: str) -> None:
self.genus = genus
self.species = species
assert_type(Pet.genus, str)
assert_type(Pet.species, str)
@@ -71,3 +71,28 @@ class BadClass1(Generic[T, T]): # E
class GoodClass1(dict[T, T]): # OK
pass
# > Type variables are applied to the defined class in the order in which
# > they first appear in any generic base classes.
T1 = TypeVar("T1")
T2 = TypeVar("T2")
T3 = TypeVar("T3")
class Parent1(Generic[T1, T2]): ...
class Parent2(Generic[T1, T2]): ...
class Child(Parent1[T1, T3], Parent2[T2, T3]): ...
def takes_parent1(x: Parent1[int, bytes]): ...
def takes_parent2(x: Parent2[str, bytes]): ...
child: Child[int, bytes, str] = Child()
takes_parent1(child) # OK
takes_parent2(child) # OK
# > A type checker should report an error when the type variable order is
# > inconsistent.
class Grandparent(Generic[T1, T2]): ...
class Parent(Grandparent[T1, T2]): ...
class BadChild(Parent[T1, T2], Grandparent[T2, T1]): ... # E
@@ -7,7 +7,7 @@ Tests for basic usage of generics.
from __future__ import annotations
from collections.abc import Sequence
from typing import Any, Generic, TypeVar, assert_type
from typing import Any, Generic, Protocol, TypeVar, assert_type
T = TypeVar("T")
@@ -157,6 +157,23 @@ def test_my_map(m1: MyMap1[str, int], m2: MyMap2[int, str]):
m1[0] # E
m2[0] # E
# > All arguments to ``Generic`` or ``Protocol`` must be type variables.
class Bad1(Generic[int]): ... # E
class Bad2(Protocol[int]): ... # E
# > All type parameters for the class must appear within the ``Generic`` or
# > ``Protocol`` type argument list.
T_co = TypeVar("T_co", covariant=True)
S_co = TypeVar("S_co", covariant=True)
class Bad3(Iterable[T_co], Generic[S_co]): ... # E
class Bad4(Iterable[T_co], Protocol[S_co]): ... # E
# > The above rule does not apply to a bare ``Protocol`` base class.
class MyIterator(Iterator[T_co], Protocol): ... # OK
# > You can use multiple inheritance with ``Generic``
@@ -100,15 +100,15 @@ assert_type(Class_TypeVarTuple[int, bool](), Class_TypeVarTuple[int, bool])
# > subtype of ``bound``. If not, the type checker should generate an
# > error.
TypeVar("Ok", bound=float, default=int) # OK
TypeVar("Invalid", bound=str, default=int) # E: the bound and default are incompatible
Ok1 = TypeVar("Ok1", bound=float, default=int) # OK
Invalid1 = TypeVar("Invalid1", bound=str, default=int) # E: the bound and default are incompatible
# > For constrained ``TypeVar``\ s, the default needs to be one of the
# > constraints. A type checker should generate an error even if it is a
# > subtype of one of the constraints.
TypeVar("Ok", float, str, default=float) # OK
TypeVar("Invalid", float, str, default=int) # E: expected one of float or str got int
Ok2 = TypeVar("Ok2", float, str, default=float) # OK
Invalid2 = TypeVar("Invalid2", float, str, default=int) # E: expected one of float or str got int
# > In generic functions, type checkers may use a type parameter's default when the
@@ -63,19 +63,19 @@ class Foo3(Generic[S1]):
# > ``T1``'s bound must be a subtype of ``T2``'s bound.
X1 = TypeVar("X1", bound=int)
TypeVar("Ok1", default=X1, bound=float) # OK
TypeVar("AlsoOk1", default=X1, bound=int) # OK
TypeVar("Invalid1", default=X1, bound=str) # E: int is not a subtype of str
Ok1 = TypeVar("Ok1", default=X1, bound=float) # OK
AlsoOk1 = TypeVar("AlsoOk1", default=X1, bound=int) # OK
Invalid1 = TypeVar("Invalid1", default=X1, bound=str) # E: int is not a subtype of str
# > The constraints of ``T2`` must be a superset of the constraints of ``T1``.
Y1 = TypeVar("Y1", bound=int)
TypeVar("Invalid2", float, str, default=Y1) # E: upper bound int is incompatible with constraints float or str
Invalid2 = TypeVar("Invalid2", float, str, default=Y1) # E: upper bound int is incompatible with constraints float or str
Y2 = TypeVar("Y2", int, str)
TypeVar("AlsoOk2", int, str, bool, default=Y2) # OK
TypeVar("AlsoInvalid2", bool, complex, default=Y2) # E: {bool, complex} is not a superset of {int, str}
AlsoOk2 = TypeVar("AlsoOk2", int, str, bool, default=Y2) # OK
AlsoInvalid2 = TypeVar("AlsoInvalid2", bool, complex, default=Y2) # E: {bool, complex} is not a superset of {int, str}
# > Type parameters are valid as parameters to generics inside of a
@@ -77,12 +77,18 @@ class Outer(Generic[T]):
class AlsoBad:
x: list[T] # E
def __init__(self, x: list[T]) -> None:
self.x = x
class Inner(Iterable[S]): # OK
...
attr: Inner[T] # OK
alias: TypeAlias = list[T] # E
def __init__(self, attr: Inner[T]) -> None:
self.attr = attr
# Test unbound type variables at global scope
global_var1: T # E
@@ -58,6 +58,9 @@ assert_type(Circle.from_config({}), Circle)
class Container(Generic[T]):
value: T
def __init__(self, value: T) -> None:
self.value = value
def set_value(self, value: T) -> Self: ...
# This should generate an error because Self isn't subscriptable.
@@ -7,13 +7,11 @@ Validates the type parameter syntax introduced in PEP 695.
# This generic class is parameterized by a TypeVar T, a
# TypeVarTuple Ts, and a ParamSpec P.
from typing import Generic, ParamSpec, Protocol, TypeVar, TypeVarTuple, assert_type
from typing import Generic, Protocol
class ChildClass[T, *Ts, **P]:
assert_type(T, TypeVar)
assert_type(Ts, TypeVarTuple)
assert_type(P, ParamSpec)
pass
class ClassA[T](Generic[T]): # E: Runtime error
@@ -51,8 +51,15 @@ class ShouldBeCovariant4(Generic[T]):
x: T
vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK
vo4_4: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1.0) # E
# This test is problematic as of Python 3.13 because of the
# newly synthesized "__replace__" method, which causes the type
# variable to be inferred as invariant rather than covariant.
# See https://github.com/python/mypy/issues/17623#issuecomment-2266312738
# for details. Until we sort this out, we'll leave this test commented
# out.
# vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK
# vo4_4: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1.0) # E
class ShouldBeCovariant5(Generic[T]):
@@ -84,7 +91,7 @@ class ShouldBeInvariant1(Generic[T]):
self._value = value
@property
def value(self):
def value(self) -> T:
return self._value
@value.setter
@@ -10,7 +10,9 @@ T = TypeVar("T")
class Node(Generic[T]):
label: T
def __init__(self, label: T | None = None) -> None: ...
def __init__(self, label: T | None = None) -> None:
if label is not None:
self.label = label
assert_type(Node(''), Node[str])
assert_type(Node(0), Node[int])
@@ -41,8 +41,8 @@ v3: Array[Time, Batch, Height, Width] = Array(
v4: Array[Height, Width] = Array(Height(1)) # E
v5: Array[Batch, Height, Width] = Array((Batch(1), Width(1))) # E
v6: Array[Time, Batch, Height, Width] = Array( # E
(Time(1), Batch(1), Width(1), Height(1))
v6: Array[Time, Batch, Height, Width] = Array( # E[v6]
(Time(1), Batch(1), Width(1), Height(1)) # E[v6]
)
@@ -122,14 +122,14 @@ class CoContra_Child1(CoContra[T_co, T_contra]): # OK
...
class CoContra_Child2(
CoContra[T_co, T_co] # E: Second type arg must be contravariant
class CoContra_Child2( # E[CoContra_Child2]: Second type arg must be contravariant
CoContra[T_co, T_co] # E[CoContra_Child2]: Second type arg must be contravariant
):
...
class CoContra_Child3(
CoContra[T_contra, T_contra] # E: First type arg must be covariant
class CoContra_Child3( # E[CoContra_Child3]: First type arg must be covariant
CoContra[T_contra, T_contra] # E[CoContra_Child3]: First type arg must be covariant
):
...
@@ -138,8 +138,8 @@ class CoContra_Child4(CoContra[T, T]): # OK
...
class CoContra_Child5(
CoContra[Co[T_co], Co[T_co]] # E: Second type arg must be contravariant
class CoContra_Child5( # E[CoContra_Child5]: Second type arg must be contravariant
CoContra[Co[T_co], Co[T_co]] # E[CoContra_Child5]: Second type arg must be contravariant
):
...
@@ -192,7 +192,7 @@ class CoToContraToContra_WithTA(Contra_TA[Co_TA[Contra_TA[T_contra]]]): # E
...
class ContraToContraToContra_WithTA(
Contra_TA[Contra_TA[Contra_TA[T_co]]] # E
class ContraToContraToContra_WithTA( # E[ContraToContraToContra_WithTA]
Contra_TA[Contra_TA[Contra_TA[T_co]]] # E[ContraToContraToContra_WithTA]
):
...
@@ -41,7 +41,7 @@ vco1_1: ShouldBeCovariant1[float] = ShouldBeCovariant1[int]() # OK
vco1_2: ShouldBeCovariant1[int] = ShouldBeCovariant1[float]() # E
class ShouldBeCovariant2[T](Sequence[T]):
class ShouldBeCovariant2[T](ShouldBeCovariant1[T]):
pass
@@ -85,7 +85,7 @@ class ShouldBeInvariant1[T]:
self._value = value
@property
def value(self):
def value(self) -> T:
return self._value
@value.setter
@@ -1,16 +1,18 @@
"""
Tests the basic typing.overload behavior described in PEP 484.
Tests the behavior of typing.overload.
"""
# Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload
# Note: The behavior of @overload is severely under-specified by PEP 484 leading
# to significant divergence in behavior across type checkers. This is something
# we will likely want to address in a future update to the typing spec. For now,
# this conformance test will cover only the most basic functionality described
# in PEP 484.
from typing import Any, Callable, Iterable, Iterator, TypeVar, assert_type, overload
from typing import (
Any,
Callable,
Iterable,
Iterator,
TypeVar,
assert_type,
overload,
)
class Bytes:
@@ -57,25 +59,3 @@ def map(
def map(func: Any, iter1: Any, iter2: Any = ...) -> Any:
pass
# At least two overload signatures should be provided.
@overload # E[func1]
def func1() -> None: # E[func1]: At least two overloads must be present
...
def func1() -> None:
pass
# > In regular modules, a series of @overload-decorated definitions must be
# > followed by exactly one non-@overload-decorated definition (for the same
# > function/method).
@overload # E[func2]
def func2(x: int) -> int: # E[func2]: no implementation
...
@overload
def func2(x: str) -> str:
...
@@ -0,0 +1,118 @@
"""
Tests consistency of overloads with implementation.
"""
from typing import Callable, Coroutine, overload
from types import CoroutineType
# > If an overload implementation is defined, type checkers should validate
# > that it is consistent with all of its associated overload signatures.
# > The implementation should accept all potential sets of arguments
# > that are accepted by the overloads and should produce all potential return
# > types produced by the overloads. In typing terms, this means the input
# > signature of the implementation should be :term:`assignable` to the input
# > signatures of all overloads, and the return type of all overloads should be
# > assignable to the return type of the implementation.
# Return type of all overloads must be assignable to return type of
# implementation:
@overload
def return_type(x: int) -> int:
...
@overload
def return_type(x: str) -> str: # E[return_type]
...
def return_type(x: int | str) -> int: # E[return_type] an overload returns `str`, not assignable to `int`
return 1
# Input signature of implementation must be assignable to signature of each
# overload. We don't attempt a thorough testing of input signature
# assignability here; see `callables_subtyping.py` for that:
@overload
def parameter_type(x: int) -> int:
...
@overload
def parameter_type(x: str) -> str: # E[parameter_type]
...
def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x`
return 1
# > Overloads are allowed to use a mixture of ``async def`` and ``def`` statements
# > within the same overload definition. Type checkers should convert
# > ``async def`` statements to a non-async signature (wrapping the return
# > type in a ``Coroutine``) before testing for implementation consistency
# > and overlapping overloads (described below).
# ...and also...
# > When a type checker checks the implementation for consistency with overloads,
# > it should first apply any transforms that change the effective type of the
# > implementation including the presence of a ``yield`` statement in the
# > implementation body, the use of ``async def``, and the presence of additional
# > decorators.
# An overload can explicitly return `Coroutine`, while the implementation is an
# `async def`:
@overload
def returns_coroutine(x: int) -> CoroutineType[None, None, int]:
...
@overload
async def returns_coroutine(x: str) -> str:
...
async def returns_coroutine(x: int | str) -> int | str:
return 1
# The implementation can explicitly return `Coroutine`, while overloads are
# `async def`:
@overload
async def returns_coroutine_2(x: int) -> int:
...
@overload
async def returns_coroutine_2(x: str) -> str:
...
def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]:
return _wrapped(x)
async def _wrapped(x: int | str) -> int | str:
return 2
# Decorator transforms are applied before checking overload consistency:
def _deco_1(f: Callable) -> Callable[[int], int]:
def wrapped(_x: int, /) -> int:
return 1
return wrapped
def _deco_2(f: Callable) -> Callable[[int | str], int | str]:
def wrapped(_x: int | str, /) -> int | str:
return 1
return wrapped
@overload
@_deco_1
def decorated() -> None:
...
@overload
def decorated(x: str, /) -> str:
...
@_deco_2
def decorated(y: bytes, z: bytes) -> bytes:
return b""
@@ -0,0 +1,229 @@
"""
Tests valid/invalid definition of overloaded functions.
"""
from abc import ABC, abstractmethod
from typing import (
final,
Protocol,
overload,
override,
)
# > At least two @overload-decorated definitions must be present.
@overload # E[func1]
def func1() -> None: # E[func1]: At least two overloads must be present
...
def func1() -> None:
pass
# > The ``@overload``-decorated definitions must be followed by an overload
# > implementation, which does not include an ``@overload`` decorator. Type
# > checkers should report an error or warning if an implementation is missing.
@overload # E[func2]
def func2(x: int) -> int: # E[func2]: no implementation
...
@overload
def func2(x: str) -> str: ...
# > Overload definitions within stub files, protocols, and on abstract methods
# > within abstract base classes are exempt from this check.
class MyProto(Protocol):
@overload
def func3(self, x: int) -> int: ...
@overload
def func3(self, x: str) -> str: ...
class MyAbstractBase(ABC):
@overload
@abstractmethod
def func4(self, x: int) -> int: ...
@overload
@abstractmethod
def func4(self, x: str) -> str: ...
# A non-abstract method in an abstract base class still requires an
# implementation:
@overload # E[not_abstract]
def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation
...
@overload
def not_abstract(self, x: str) -> str: ...
# > If one overload signature is decorated with ``@staticmethod`` or
# > ``@classmethod``, all overload signatures must be similarly decorated. The
# > implementation, if present, must also have a consistent decorator. Type
# > checkers should report an error if these conditions are not met.
class C:
@overload # E[func5]
@staticmethod
def func5(x: int, /) -> int: # E[func5]
...
@overload
@staticmethod
def func5(x: str, /) -> str: # E[func5]
...
def func5(*args: object) -> int | str: # E[func5]
return 1
@overload # E[func6]
@classmethod
def func6(cls, x: int, /) -> int: # E[func6]
...
@overload
def func6(self, x: str, /) -> str: # E[func6]
...
@classmethod
def func6(cls, *args: int | str) -> int | str: # E[func6]
return 1
# > If a ``@final`` or ``@override`` decorator is supplied for a function with
# > overloads, the decorator should be applied only to the overload
# > implementation if it is present. If an overload implementation isn't present
# > (for example, in a stub file), the ``@final`` or ``@override`` decorator
# > should be applied only to the first overload. Type checkers should enforce
# > these rules and generate an error when they are violated. If a ``@final`` or
# > ``@override`` decorator follows these rules, a type checker should treat the
# > decorator as if it is present on all overloads.
class Base:
# This is a good definition of an overloaded final method (@final decorator
# on implementation only):
@overload
def final_method(self, x: int) -> int: ...
@overload
def final_method(self, x: str) -> str: ...
@final
def final_method(self, x: int | str) -> int | str: ...
# The @final decorator should not be on one of the overloads:
@overload # E[invalid_final] @final should be on implementation only
@final
def invalid_final(self, x: int) -> int: # E[invalid_final]
...
@overload
def invalid_final(self, x: str) -> str: # E[invalid_final]
...
def invalid_final(self, x: int | str) -> int | str: ...
# The @final decorator should not be on multiple overloads and
# implementation:
@overload # E[invalid_final_2+]: @final should be on implementation only
@final # E[invalid_final_2+]
def invalid_final_2(self, x: int) -> int: # E[invalid_final_2+]
...
@overload
@final # E[invalid_final_2+]
def invalid_final_2(self, x: str) -> str: # E[invalid_final_2+]
...
@final
def invalid_final_2(self, x: int | str) -> int | str: ...
# These methods are just here for the @override test below. We use an
# overload because mypy doesn't like overriding a non-overloaded method
# with an overloaded one, even if LSP isn't violated. That could be its own
# specification question, but it's not what we're trying to test here:
@overload
def good_override(self, x: int) -> int: ...
@overload
def good_override(self, x: str) -> str: ...
def good_override(self, x: int | str) -> int | str: ...
@overload
def to_override(self, x: int) -> int: ...
@overload
def to_override(self, x: str) -> str: ...
def to_override(self, x: int | str) -> int | str: ...
class Child(Base): # E[override-final]
# The correctly-decorated @final method `Base.final_method` should cause an
# error if overridden in a child class (we use an overload here to avoid
# questions of override LSP compatibility and focus only on the override):
@overload # E[override-final]
def final_method(self, x: int) -> int: ...
@overload
def final_method(self, x: str) -> str: ...
def final_method( # E[override-final] can't override final method
self, x: int | str
) -> int | str: # E[override-final] can't override final method
...
# This is the right way to mark an overload as @override (decorate
# implementation only), so the use of @override should cause an error
# (because there's no `Base.bad_override` method):
@overload # E[bad_override] marked as override but doesn't exist in base
def bad_override(self, x: int) -> int: # E[bad_override]
...
@overload
def bad_override(self, x: str) -> str: ...
@override
def bad_override(self, x: int | str) -> int | str: # E[bad_override]
...
# This is also a correctly-decorated overloaded @override, which is
# overriding a method that does exist in the base, so there should be no
# error. We need both this test and the previous one, because in the
# previous test, an incorrect error about the use of @override decorator
# could appear on the same line as the expected error about overriding a
# method that doesn't exist in base:
@overload
def good_override(self, x: int) -> int: ...
@overload
def good_override(self, x: str) -> str: ...
@override
def good_override(self, x: int | str) -> int | str: ...
# This is the wrong way to use @override with an overloaded method, and
# should emit an error:
@overload # E[override_impl+]: @override should appear only on implementation
@override # E[override_impl+]
def to_override(self, x: int) -> int: ... # E[override_impl+]
@overload
@override # E[override_impl+]
def to_override(self, x: str) -> str: ... # E[override_impl+]
@override
def to_override(self, x: int | str) -> int | str: ...
@@ -0,0 +1,150 @@
"""
Tests valid/invalid definition of overloaded functions in stub files, where the
rules differ from non-stubs, since an implementation is not required.
"""
from typing import (
final,
overload,
override,
)
# > At least two @overload-decorated definitions must be present.
@overload # E[func1]
def func1() -> None: # E[func1]: At least two overloads must be present
...
# > The ``@overload``-decorated definitions must be followed by an overload
# > implementation, which does not include an ``@overload`` decorator. Type
# > checkers should report an error or warning if an implementation is missing.
# > Overload definitions within stub files, protocols, and on abstract methods
# > within abstract base classes are exempt from this check.
@overload
def func2(x: int) -> int: ...
@overload
def func2(x: str) -> str: ...
# > If one overload signature is decorated with ``@staticmethod`` or
# > ``@classmethod``, all overload signatures must be similarly decorated. The
# > implementation, if present, must also have a consistent decorator. Type
# > checkers should report an error if these conditions are not met.
class C:
@overload # E[func5]
def func5(self, x: int, /) -> int: # E[func5]
...
@overload
@staticmethod
def func5(x: str, /) -> str: # E[func5]
...
@overload # E[func6]
@classmethod
def func6(cls, x: int, /) -> int: # E[func6]
...
@overload
def func6(self, *args: str) -> str: # E[func6]
...
# > If a ``@final`` or ``@override`` decorator is supplied for a function with
# > overloads, the decorator should be applied only to the overload
# > implementation if it is present. If an overload implementation isn't present
# > (for example, in a stub file), the ``@final`` or ``@override`` decorator
# > should be applied only to the first overload. Type checkers should enforce
# > these rules and generate an error when they are violated. If a ``@final`` or
# > ``@override`` decorator follows these rules, a type checker should treat the
# > decorator as if it is present on all overloads.
class Base:
# This is a good definition of an overloaded final method in a stub (@final
# decorator on first overload only):
@overload
@final
def final_method(self, x: int) -> int: ...
@overload
def final_method(self, x: str) -> str: ...
# The @final decorator should not be on multiple overloads:
@overload # E[invalid_final] @final should be on first overload
@final
def invalid_final(self, x: int) -> int: # E[invalid_final]
...
@overload # E[invalid_final]
@final
def invalid_final(self, x: str) -> str: # E[invalid_final]
...
@overload
def invalid_final(self, x: bytes) -> bytes: ...
# The @final decorator should not be on all overloads:
@overload # E[invalid_final_2] @final should be on first overload
@final
def invalid_final_2(self, x: int) -> int: # E[invalid_final_2]
...
@overload # E[invalid_final_2]
@final
def invalid_final_2(self, x: str) -> str: ... # E[invalid_final_2]
# These methods are just here for the @override test below. We use an
# overload because mypy doesn't like overriding a non-overloaded method
# with an overloaded one, even if LSP isn't violated. That could be its own
# specification question, but it's not what we're trying to test here:
@overload
def good_override(self, x: int) -> int: ...
@overload
def good_override(self, x: str) -> str: ...
@overload
def to_override(self, x: int) -> int: ...
@overload
def to_override(self, x: str) -> str: ...
class Child(Base): # E[override-final]
# The correctly-decorated @final method `Base.final_method` should cause an
# error if overridden in a child class (we use an overload here to avoid
# questions of override LSP compatibility and focus only on the override):
@overload # E[override-final]
def final_method(self, x: int) -> int: # E[override-final]
...
@overload
def final_method( # E[override-final] can't override final method
self, x: str
) -> str: # E[override-final] can't override final method
...
# This is the right way to mark an overload as @override (decorate first
# overload only), so the use of @override should cause an error (because
# there's no `Base.bad_override` method):
@overload # E[bad_override] marked as override but doesn't exist in base
@override
def bad_override(self, x: int) -> int: # E[bad_override]
...
@overload
def bad_override(self, x: str) -> str: ...
# This is also a correctly-decorated overloaded @override, which is
# overriding a method that does exist in the base, so there should be no
# error. We need both this test and the previous one, because in the
# previous test, an incorrect error about the use of @override decorator
# could appear on the same line as the expected error about overriding a
# method that doesn't exist in base:
@overload
@override
def good_override(self, x: int) -> int: ...
@overload
def good_override(self, x: str) -> str: ...
# This is the wrong way to use @override with an overloaded method, and
# should emit an error:
@overload # E[override_impl]: @override should appear only on first overload
def to_override(self, x: int) -> int: ...
@overload
@override
def to_override( # E[override_impl]: @override should appear only on first overload
self, x: str
) -> str: # E[override_impl]: @override should appear only on first overload
...
@@ -0,0 +1,345 @@
"""
Tests for evaluation of calls to overloaded functions.
"""
from enum import Enum
from typing import Any, assert_type, Literal, overload, TypeVar
# mypy: disable-error-code=overload-overlap
T = TypeVar("T")
# > Step 1: Examine the argument list to determine the number of
# > positional and keyword arguments. Use this information to eliminate any
# > overload candidates that are not plausible based on their
# > input signatures.
# (There is no way to observe via conformance tests whether an implementation
# performs this step separately from the argument-type-testing step 2 below, so
# the separation of step 1 from step 2 is purely a presentation choice for the
# algorithm, not a conformance requirement.)
@overload
def example1_1(x: int, y: str) -> int: ...
@overload
def example1_1(x: str) -> str: ...
def example1_1(x: int | str, y: str = "") -> int | str:
return 1
# > - If no candidate overloads remain, generate an error and stop.
example1_1() # E: no matching overload
# > - If only one candidate overload remains, it is the winning match. Evaluate
# > it as if it were a non-overloaded function call and stop.
ret1 = example1_1(1, "")
assert_type(ret1, int)
example1_1(1, 1) # E: Literal[1] not assignable to str
ret3 = example1_1("")
assert_type(ret3, str)
example1_1(1) # E: Literal[1] not assignable to str
@overload
def example1_2(b: Literal[True] = ...) -> int: ...
@overload
def example1_2(b: bool) -> float: ...
def example1_2(b: bool = True) -> float: ...
def check_example1_2() -> None:
assert_type(example1_2(), int)
# > Step 2: Evaluate each remaining overload as a regular (non-overloaded)
# > call to determine whether it is compatible with the supplied
# > argument list. Unlike step 1, this step considers the types of the parameters
# > and arguments. During this step, do not generate any user-visible errors.
# > Simply record which of the overloads result in evaluation errors.
@overload
def example2(x: int, y: str, z: int) -> str: ...
@overload
def example2(x: int, y: int, z: int) -> int: ...
def example2(x: int, y: int | str, z: int) -> int | str:
return 1
# > - If only one overload evaluates without error, it is the winning match.
# > Evaluate it as if it were a non-overloaded function call and stop.
ret5 = example2(1, 2, 3)
assert_type(ret5, int)
# > Step 3: If step 2 produces errors for all overloads, perform
# > "argument type expansion". Union types can be expanded
# > into their constituent subtypes. For example, the type ``int | str`` can
# > be expanded into ``int`` and ``str``.
# > - If all argument lists evaluate successfully, combine their
# > respective return types by union to determine the final return type
# > for the call, and stop.
def check_expand_union(v: int | str) -> None:
ret1 = example2(1, v, 1)
assert_type(ret1, int | str)
# > - If argument expansion has been applied to all arguments and one or
# > more of the expanded argument lists cannot be evaluated successfully,
# > generate an error and stop.
def check_expand_union_2(v: int | str) -> None:
example2(v, v, 1) # E: no overload matches (str, ..., ...)
# > 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``.
@overload
def expand_bool(x: Literal[False]) -> Literal[0]: ...
@overload
def expand_bool(x: Literal[True]) -> Literal[1]: ...
def expand_bool(x: bool) -> int:
return int(x)
def check_expand_bool(v: bool) -> None:
ret1 = expand_bool(v)
assert_type(ret1, Literal[0, 1])
# > 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should
# > be expanded into their literal members.
class Color(Enum):
RED = 1
BLUE = 1
@overload
def expand_enum(x: Literal[Color.RED]) -> Literal[0]: ...
@overload
def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]: ...
def expand_enum(x: Color) -> int:
return x.value
def check_expand_enum(v: Color) -> None:
ret1 = expand_enum(v)
assert_type(ret1, Literal[0, 1])
# > 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``.
@overload
def expand_type_union(x: type[int]) -> int: ...
@overload
def expand_type_union(x: type[str]) -> str: ...
def expand_type_union(x: type[int] | type[str]) -> int | str:
return 1
def check_expand_type_union(v: type[int | str]) -> None:
ret1 = expand_type_union(v)
assert_type(ret1, int | str)
# > 5. Tuples of known length that contain expandable types should be expanded
# > into all possible combinations of their element types. For example, the type
# > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``,
# > ``(int, Literal[False])``, ``(str, Literal[True])``, and
# > ``(str, Literal[False])``.
@overload
def expand_tuple(x: tuple[int, int]) -> int: ...
@overload
def expand_tuple(x: tuple[int, str]) -> str: ...
def expand_tuple(x: tuple[int, int | str]) -> int | str:
return 1
def check_expand_tuple(v: int | str) -> None:
ret1 = expand_tuple((1, v))
assert_type(ret1, int | str)
# > Step 4: If the argument list is compatible with two or more overloads,
# > determine whether one or more of the overloads has a variadic parameter
# > (either ``*args`` or ``**kwargs``) that maps to a corresponding argument
# > that supplies an indeterminate number of positional or keyword arguments.
# > If so, eliminate overloads that do not have a variadic parameter.
@overload
def variadic(x: int, /) -> str: ...
@overload
def variadic(x: int, y: int, /, *args: int) -> int: ...
def variadic(*args: int) -> int | str:
return 1
# > - If this results in only one remaining candidate overload, it is
# > the winning match. Evaluate it as if it were a non-overloaded function
# > call and stop.
def check_variadic(v: list[int]) -> None:
ret1 = variadic(*v)
assert_type(ret1, int)
# > Step 5: For all arguments, determine whether all possible
# > :term:`materializations <materialize>` of the argument's type are assignable to
# > the corresponding parameter type for each of the remaining overloads. If so,
# > eliminate all of the subsequent remaining overloads.
@overload
def example4(x: list[int], y: int) -> list[int]: ...
@overload
def example4(x: list[str], y: str) -> list[int]: ...
@overload
def example4(x: int, y: int) -> list[str]: ...
def example4(x: list[int] | list[str] | int, y: int | str) -> list[int] | list[str]:
return []
def check_example4(v1: list[Any], v2: Any) -> None:
ret1 = example4(v1, v2)
assert_type(ret1, list[int])
ret2 = example4(v2, 1)
assert_type(ret2, Any)
@overload
def example5(obj: list[int]) -> list[int]: ...
@overload
def example5(obj: list[str]) -> list[str]: ...
def example5(obj: Any) -> list[Any]:
return []
def check_example5(b: list[Any]) -> None:
assert_type(example5(b), Any)
@overload
def example6(a: int, b: Any) -> float: ...
@overload
def example6(a: float, b: T) -> T: ...
def example6(a: float, b: T) -> T: ...
def check_example6(a: list[Any], b: Any, c: str) -> None:
m: list[int] = []
# All possible materializations of list[Any] are
# assignable to Any, so this matches the first overload
# and eliminates all subsequent overloads.
v1 = example6(1, a)
assert_type(v1, float)
# All possible materializations of Any are
# assignable to Any, so this matches the first overload
# and eliminates all subsequent overloads.
v2 = example6(1, b)
assert_type(v2, float)
# All possible materializations of list[int] are
# assignable to Any, so this matches the first overload
# and eliminates all subsequent overloads.
v3 = example6(1, m)
assert_type(v3, float)
v4 = example6(1.0, c)
assert_type(v4, str)
v5 = example6(1.0, b)
assert_type(v5, Any)
v6 = example6(1.0, m)
assert_type(v6, list[int])
@overload
def example7(x: list[Any], y: int) -> list[int]: ...
@overload
def example7(x: list[Any], y: str) -> list[str]: ...
def example7(x: list[Any], y: int | str) -> list[int] | list[str]:
return []
def check_example7(v1: list[Any], v2: Any) -> None:
ret1 = example7(v1, 1)
assert_type(ret1, list[int])
ret2 = example7(v1, "")
assert_type(ret2, list[str])
ret3 = example7(v1, v2)
assert_type(ret3, Any)
@@ -93,6 +93,9 @@ class ConcreteC2:
class CMeta(type):
attr1: int
def __init__(self, attr1: int) -> None:
self.attr1 = attr1
class ConcreteC3(metaclass=CMeta):
pass
@@ -54,6 +54,7 @@ class RGB(Protocol):
class Point(RGB):
def __init__(self, red: int, green: int, blue: str) -> None:
self.rgb = red, green, blue # E: 'blue' must be 'int'
self.other = 0
p = Point(0, 0, "") # E: Cannot instantiate abstract class
@@ -80,7 +81,10 @@ class Proto3(Proto2, Protocol):
class Concrete1(Proto1):
...
def __init__(self):
self.cm1 = 1
self.im1 = 1
self.im3 = 3
c1 = Concrete1() # E: cannot instantiate abstract class
@@ -98,7 +102,9 @@ class Concrete3(Proto1, Proto3):
cm1 = 3
def __init__(self):
im1 = 0
self.im1 = 0
self.cm10 = 10
self.cm11 = 11
c3 = Concrete3() # E: cannot instantiate abstract class
@@ -142,6 +142,6 @@ class ConcreteHasProperty4:
hp1: HasPropertyProto = ConcreteHasProperty1() # OK
hp2: HasPropertyProto = ConcreteHasProperty2() # OK
hp2: HasPropertyProto = ConcreteHasProperty2() # E
hp3: HasPropertyProto = ConcreteHasProperty3() # E
hp4: HasPropertyProto = ConcreteHasProperty4() # E
@@ -49,8 +49,8 @@ s3: SizedAndClosable1 = SizedAndClosable3() # OK
s4: SizedAndClosable2 = SizedAndClosable3() # OK
s5: Sized = SCConcrete1() # OK
s6: SizedAndClosable1 = SCConcrete2() # E: doesn't implement close
s7: SizedAndClosable2 = SCConcrete2() # E: doesn't implement close
s6: SizedAndClosable1 = SCConcrete2() # E: doesn't implement `__len__`
s7: SizedAndClosable2 = SCConcrete2() # E: doesn't implement `__len__`
s8: SizedAndClosable3 = SCConcrete2() # E: SizedAndClosable3 is not a protocol
@@ -138,18 +138,33 @@ N(x="", y="") # E
def func2() -> None:
global ID1
ID1 = 2 # E: cannot modify Final value
ID1 = 2 # E: cannot modify Final value
x: Final = 3
x += 1 # E: cannot modify Final value
x += 1 # E: cannot modify Final value
a = (x := 4) # E: cannot modify Final value
for x in [1, 2, 3]: # E: cannot modify Final value
for x in [1, 2, 3]: # E: cannot modify Final value
pass
with open("FileName") as x: # E: cannot modify Final value
with open("FileName") as x: # E: cannot modify Final value
pass
(a, x) = (1, 2) # E: cannot modify Final value
# > If a module declares a ``Final`` variable and another module imports that
# > variable in an import statement by name or wildcard, the imported symbol
# > inherits the ``Final`` type qualifier. Any attempt to assign a different value
# > to this symbol should be flagged as an error by a type checker.
from _qualifiers_final_annotation_1 import TEN
TEN = 9 # E: Cannot redefine Final value
from _qualifiers_final_annotation_2 import *
PI = 3.14159 # E: Cannot redefine Final value
@@ -56,6 +56,10 @@ class ClassA:
x: NoReturn
y: list[NoReturn]
def __init__(self, x: NoReturn, y: list[NoReturn]) -> None:
self.x = x
self.y = y
# Never is compatible with all types.
@@ -7,7 +7,7 @@ Tests type compatibility rules for tuples.
# > Because tuple contents are immutable, the element types of a tuple are covariant.
from typing import Any, Iterable, Never, Sequence, TypeVar, assert_type
from typing import Any, Iterable, Never, Sequence, TypeAlias, TypeVar, assert_type
def func1(t1: tuple[float, complex], t2: tuple[int, int]):
@@ -67,19 +67,23 @@ def func4(
# NOTE: This type narrowing functionality is optional, not mandated.
Func5Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]
def func5(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]):
def func5(val: Func5Input):
if len(val) == 1:
# Type can be narrowed to tuple[int].
assert_type(val, tuple[int]) # tuple[int]
assert_type(val, tuple[int]) # E[func5_1]
assert_type(val, Func5Input) # E[func5_1]
if len(val) == 2:
# Type can be narrowed to tuple[str, str] | tuple[int, int].
assert_type(val, tuple[str, str] | tuple[int, int])
assert_type(val, tuple[str, str] | tuple[int, int]) # E[func5_2]
assert_type(val, Func5Input) # E[func5_2]
if len(val) == 3:
# Type can be narrowed to tuple[int, str, int].
assert_type(val, tuple[int, str, int])
assert_type(val, tuple[int, str, int]) # E[func5_3]
assert_type(val, Func5Input) # E[func5_3]
# > This property may also be used to safely narrow tuple types within a match
@@ -87,20 +91,25 @@ def func5(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int])
# NOTE: This type narrowing functionality is optional, not mandated.
Func6Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]
def func6(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]):
def func6(val: Func6Input):
match val:
case (x,):
# Type can be narrowed to tuple[int].
assert_type(val, tuple[int]) # tuple[int]
# Type may be narrowed to tuple[int].
assert_type(val, tuple[int]) # E[func6_1]
assert_type(val, Func6Input) # E[func6_1]
case (x, y):
# Type can be narrowed to tuple[str, str] | tuple[int, int].
assert_type(val, tuple[str, str] | tuple[int, int])
# Type may be narrowed to tuple[str, str] | tuple[int, int].
assert_type(val, tuple[str, str] | tuple[int, int]) # E[func6_2]
assert_type(val, Func6Input) # E[func6_2]
case (x, y, z):
# Type can be narrowed to tuple[int, str, int].
assert_type(val, tuple[int, str, int])
# Type may be narrowed to tuple[int, str, int].
assert_type(val, tuple[int, str, int]) # E[func6_3]
assert_type(val, Func6Input) # E[func6_3]
# > Type checkers may safely use this equivalency rule (tuple expansion)
@@ -108,13 +117,17 @@ def func6(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int])
# NOTE: This type narrowing functionality is optional, not mandated.
Func7Input: TypeAlias = tuple[int | str, int | str]
def func7(subj: tuple[int | str, int | str]):
def func7(subj: Func7Input):
match subj:
case x, str():
assert_type(subj, tuple[int | str, str])
assert_type(subj, tuple[int | str, str]) # E[func7_1]
assert_type(subj, Func7Input) # E[func7_1]
case y:
assert_type(subj, tuple[int | str, int])
assert_type(subj, tuple[int | str, int]) # E[func7_2]
assert_type(subj, Func7Input) # E[func7_2]
# > The tuple class derives from Sequence[T_co] where ``T_co`` is a covariant
@@ -57,6 +57,6 @@ def func3(t: tuple[*Ts]):
t11: tuple[Unpack[tuple[str]], Unpack[tuple[str]]] # OK
t12: tuple[Unpack[tuple[str, Unpack[tuple[str, ...]]]]] # OK
t13: tuple[Unpack[tuple[str, ...]], Unpack[tuple[int, ...]]] # E
t14: tuple[
Unpack[tuple[str, Unpack[tuple[str, ...]]]], Unpack[tuple[int, ...]] # E
t14: tuple[ # E[t14]
Unpack[tuple[str, Unpack[tuple[str, ...]]]], Unpack[tuple[int, ...]] # E[t14]
]
@@ -30,13 +30,13 @@ class BadTypedDict1(TypedDict):
pass
# Methods are not allowed, so this should generate an error.
@classmethod # E
def method2(cls):
@classmethod # E[method2]
def method2(cls): # E[method2]
pass
# Methods are not allowed, so this should generate an error.
@staticmethod # E
def method3():
@staticmethod # E[method3]
def method3(): # E[method3]
pass
@@ -54,7 +54,7 @@ class BadTypedDict3(TypedDict, other=True): # E
T = TypeVar("T")
class GenericTypedDict(Generic[T]):
class GenericTypedDict(TypedDict, Generic[T]):
name: str
value: T
@@ -11,6 +11,9 @@ from typing import Annotated, NotRequired, Required, TypedDict
class NotTypedDict:
x: Required[int] # E: Required not allowed in this context
def __init__(self, x: int) -> None:
self.x = x
def func1(
x: NotRequired[int], # E: NotRequired not allowed in this context