← Back to index

generics_defaults.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 5
FP: 8
FN: 1
Optional: 0 / 0
1"""
2Tests for basic usage of default values for TypeVar-like's.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#defaults-for-type-parameters.
6
7from typing import Any, Callable, Generic, Self, Unpack, assert_type
8from typing_extensions import TypeVar, ParamSpec, TypeVarTuple
9
11DefaultStrT = TypeVar("DefaultStrT", default=str)
12DefaultIntT = TypeVar("DefaultIntT", default=int)
13DefaultBoolT = TypeVar("DefaultBoolT", default=bool)
14T = TypeVar("T")
15T1 = TypeVar("T1")
16T2 = TypeVar("T2")
18# > The order for defaults should follow the standard function parameter
19# > rules, so a type parameter with no ``default`` cannot follow one with
20# > a ``default`` value. Doing so may raise a ``TypeError`` at runtime,
21# > and a type checker should flag this as an error.
24class NonDefaultFollowsDefault(Generic[DefaultStrT, T]): ... # E: non-default TypeVars cannot follow ones with defaults
[invalid-generic-class] Type parameter `T` without a default cannot follow earlier parameter `DefaultStrT` with a default
27class NoNonDefaults(Generic[DefaultStrT, DefaultIntT]):
28 x: DefaultStrT
29 y: DefaultIntT
32def test_no_non_defaults(a: NoNonDefaults, b: NoNonDefaults[str], c: NoNonDefaults[str, int]):
33 assert_type(a.x, str)
34 assert_type(a.y, int)
36 assert_type(b.x, str)
37 assert_type(b.y, int)
39 assert_type(c.x, str)
40 assert_type(c.y, int)
43class OneDefault(Generic[T, DefaultBoolT]):
44 x: T
45 y: DefaultBoolT
48def test_one_default(a: OneDefault[float], b: OneDefault[float, bool]):
49 assert_type(a.x, float)
50 assert_type(a.y, bool)
52 assert_type(b.x, float)
53 assert_type(b.y, bool)
56class AllTheDefaults(Generic[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]):
57 x1: T1
58 x2: T2
59 x3: DefaultStrT
60 x4: DefaultIntT
61 x5: DefaultBoolT
64def test_all_the_defaults(
65 a: AllTheDefaults,
66 b: AllTheDefaults[int], # E: expected 2 arguments to AllTheDefaults
[invalid-type-arguments] No type argument provided for required type variable `T2` of class `AllTheDefaults`
67 c: AllTheDefaults[int, complex],
68 d: AllTheDefaults[int, complex, str, int, bool],
69 e: AllTheDefaults[int, complex, str],
70 f: AllTheDefaults[int, complex, str, int],
71 g: AllTheDefaults[int, complex, str, int, bool],
72):
73 assert_type(a.x1, Any)
74 assert_type(a.x2, Any)
75 assert_type(a.x3, str)
76 assert_type(a.x4, int)
77 assert_type(a.x5, bool)
79 assert_type(c.x1, int)
80 assert_type(c.x2, complex)
81 assert_type(c.x3, str)
82 assert_type(c.x4, int)
83 assert_type(c.x5, bool)
85 assert_type(d.x1, int)
86 assert_type(d.x2, complex)
87 assert_type(d.x3, str)
88 assert_type(d.x4, int)
89 assert_type(d.x5, bool)
91 assert_type(e.x1, int)
92 assert_type(e.x2, complex)
93 assert_type(e.x3, str)
94 assert_type(e.x4, int)
95 assert_type(e.x5, bool)
97 assert_type(f.x1, int)
98 assert_type(f.x2, complex)
99 assert_type(f.x3, str)
100 assert_type(f.x4, int)
101 assert_type(f.x5, bool)
103 assert_type(g.x1, int)
104 assert_type(g.x2, complex)
105 assert_type(g.x3, str)
106 assert_type(g.x4, int)
107 assert_type(g.x5, bool)
110# > ``ParamSpec`` defaults are defined using the same syntax as
111# > ``TypeVar`` \ s but use a ``list`` of types or an ellipsis
112# > literal "``...``" or another in-scope ``ParamSpec``.
114DefaultP = ParamSpec("DefaultP", default=[str, int])
117class Class_ParamSpec(Generic[DefaultP]):
118 x: Callable[DefaultP, None]
121def test_param_spec_defaults(a: Class_ParamSpec):
122 assert_type(a.x, Callable[[str, int], None])
Unexpected error [type-assertion-failure] Type `(**DefaultP@Class_ParamSpec) -> None` does not match asserted type `(str, int, /) -> None`
123 assert_type(Class_ParamSpec(), Class_ParamSpec[str, int])
124 assert_type(Class_ParamSpec[[bool, bool]](), Class_ParamSpec[bool, bool])
127# > ``TypeVarTuple`` defaults are defined using the same syntax as
128# > ``TypeVar`` \ s, but instead of a single type, they use an unpacked tuple
129# > of types or an unpacked, in-scope ``TypeVarTuple`` (see `Scoping Rules`_).
131DefaultTs = TypeVarTuple("DefaultTs", default=Unpack[tuple[str, int]])
134class Class_TypeVarTuple(Generic[*DefaultTs]):
135 x: tuple[*DefaultTs]
138def test_type_var_tuple_defaults(a: Class_TypeVarTuple):
139 assert_type(a.x, tuple[str, int])
Unexpected error [type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int]`
140 assert_type(Class_TypeVarTuple(), Class_TypeVarTuple[str, int])
Unexpected error [type-assertion-failure] Type `Class_TypeVarTuple` does not match asserted type `@Todo`
141 assert_type(Class_TypeVarTuple[int, bool](), Class_TypeVarTuple[int, bool])
144AnotherDefaultTs = TypeVarTuple("AnotherDefaultTs", default=Unpack[DefaultTs])
147# > If both ``bound`` and ``default`` are passed, ``default`` must be a
148# > subtype of ``bound``. If not, the type checker should generate an
149# > error.
151Ok1 = TypeVar("Ok1", bound=float, default=int) # OK
152Invalid1 = TypeVar("Invalid1", bound=str, default=int) # E: the bound and default are incompatible
[invalid-type-variable-default] TypeVar default is not assignable to the TypeVar's upper bound
154# > For constrained ``TypeVar``\ s, the default needs to be one of the
155# > constraints. A type checker should generate an error even if it is a
156# > subtype of one of the constraints.
158Ok2 = TypeVar("Ok2", float, str, default=float) # OK
159Invalid2 = TypeVar("Invalid2", float, str, default=int) # E: expected one of float or str got int
[invalid-type-variable-default] TypeVar default is inconsistent with the TypeVar's constraints: `int` is not one of the constraints of `Invalid2`
162# > In generic functions, type checkers may use a type parameter's default when the
163# > type parameter cannot be solved to anything. We leave the semantics of this
164# > usage unspecified, as ensuring the ``default`` is returned in every code path
165# > where the type parameter can go unsolved may be too hard to implement. Type
166# > checkers are free to either disallow this case or experiment with implementing
167# > support.
169T4 = TypeVar("T4", default=int)
172def func1(x: int | set[T4]) -> T4:
173 raise NotImplementedError
176assert_type(func1(0), int) # E[optional-default-use]
177assert_type(func1(0), Any) # E[optional-default-use]
Tag 'optional-default-use' [type-assertion-failure] Type `int` does not match asserted type `Any`
180# > A ``TypeVar`` that immediately follows a ``TypeVarTuple`` is not allowed
181# > to have a default, because it would be ambiguous whether a type argument
182# > should be bound to the ``TypeVarTuple`` or the defaulted ``TypeVar``.
184Ts = TypeVarTuple("Ts")
185T5 = TypeVar("T5", default=bool)
188class Foo5(Generic[*Ts, T5]): ... # E
Expected a ty diagnostic for this line
191# > It is allowed to have a ``ParamSpec`` with a default following a
192# > ``TypeVarTuple`` with a default, as there can be no ambiguity between a
193# > type argument for the ``ParamSpec`` and one for the ``TypeVarTuple``.
195P = ParamSpec("P", default=[float, bool])
198class Foo6(Generic[*Ts, P]):
199 x: tuple[*Ts]
200 y: Callable[P, None]
Unexpected error [invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
203def test_foo6(a: Foo6[int, str], b: Foo6[int, str, [bytes]]):
204 assert_type(a.x, tuple[int, str])
Unexpected error [type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`
205 assert_type(a.y, Callable[[float, bool], None])
Unexpected error [type-assertion-failure] Type `@Todo` does not match asserted type `(int | float, bool, /) -> None`
207 assert_type(b.x, tuple[int, str])
Unexpected error [type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`
208 assert_type(b.y, Callable[[bytes], None])
Unexpected error [type-assertion-failure] Type `@Todo` does not match asserted type `(bytes, /) -> None`
211# > Type parameter defaults should be bound by attribute access
212# > (including call and subscript).
215class Foo7(Generic[DefaultIntT]):
216 def meth(self, /) -> Self:
217 return self
219 attr: DefaultIntT
222foo7 = Foo7()
223assert_type(Foo7.meth(foo7), Foo7[int])
224assert_type(Foo7().attr, int)