← Back to index

generics_basic.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 9
FP: 0
FN: 4
Optional: 0 / 0
1"""
2Tests for basic usage of generics.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#introduction
6
7from __future__ import annotations
8
9from collections.abc import Sequence
10from typing import Any, Generic, Protocol, TypeVar, assert_type
12T = TypeVar("T")
14# > Generics can be parameterized by using a factory available in
15# > ``typing`` called ``TypeVar``.
18def first(l: Sequence[T]) -> T:
19 return l[0]
22def test_first(seq_int: Sequence[int], seq_str: Sequence[str]) -> None:
23 assert_type(first(seq_int), int)
24 assert_type(first(seq_str), str)
27# > ``TypeVar`` supports constraining parametric types to a fixed set of
28# > possible types
30AnyStr = TypeVar("AnyStr", str, bytes)
33def concat(x: AnyStr, y: AnyStr) -> AnyStr:
34 return x + y
37def test_concat(s: str, b: bytes, a: Any) -> None:
38 concat(s, s) # OK
39 concat(b, b) # OK
40 concat(s, b) # E
Expected a ty diagnostic for this line
41 concat(b, s) # E
Expected a ty diagnostic for this line
43 concat(s, a) # OK
44 concat(a, b) # OK
47# > Specifying a single constraint is disallowed.
49BadConstraint1 = TypeVar("BadConstraint1", str) # E
[invalid-legacy-type-variable] A `TypeVar` cannot have exactly one constraint
51# > Note: those types cannot be parameterized by type variables
54class Test(Generic[T]):
55 BadConstraint2 = TypeVar("BadConstraint2", str, list[T]) # E
[invalid-type-variable-constraints] TypeVar constraint cannot be generic
58# > Subtypes of types constrained by a type variable should be treated
59# > as their respective explicitly listed base types in the context of the
60# > type variable.
63class MyStr(str): ...
66def test_concat_subtype(s: str, b: bytes, a: Any, m: MyStr) -> None:
67 assert_type(concat(m, m), str)
68 assert_type(concat(m, s), str)
69 concat(m, b) # E
Expected a ty diagnostic for this line
71 # TODO: should these be str or Any?
72 # reveal_type(concat(m, a))
73 # reveal_type(concat(a, m))
76# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#user-defined-generic-classes
78# > You can include a ``Generic`` base class to define a user-defined class
79# > as generic.
81from logging import Logger
82from collections.abc import Iterable
85class LoggedVar(Generic[T]):
86 def __init__(self, value: T, name: str, logger: Logger) -> None:
87 self.name = name
88 self.logger = logger
89 self.value = value
91 def set(self, new: T) -> None:
92 self.log("Set " + repr(self.value))
93 self.value = new
95 def get(self) -> T:
96 self.log("Get " + repr(self.value))
97 return self.value
99 def log(self, message: str) -> None:
100 self.logger.info("{}: {}".format(self.name, message))
103def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
104 for var in vars:
105 var.set(0)
106 assert_type(var.get(), int)
109# > A generic type can have any number of type variables, and type variables
110# > may be constrained.
112S = TypeVar("S")
115class Pair1(Generic[T, S]): ...
118# > Each type variable argument to ``Generic`` must be distinct.
121class Pair2(Generic[T, T]): # E
[invalid-generic-class] Type parameter `T` cannot appear multiple times in `Generic` subscription
122 ...
125# > The ``Generic[T]`` base class is redundant in simple cases where you
126# > subclass some other generic class and specify type variables for its
127# > parameters.
129from collections.abc import Iterator, Mapping
132class MyIter1(Iterator[T]): ...
135class MyIter2(Iterator[T], Generic[T]): ...
138def test_my_iter(m1: MyIter1[int], m2: MyIter2[int]):
139 assert_type(next(m1), int)
140 assert_type(next(m2), int)
143K = TypeVar("K")
144V = TypeVar("V")
147class MyMap1(Mapping[K, V], Generic[K, V]): ...
150class MyMap2(Mapping[K, V], Generic[V, K]): ...
153def test_my_map(m1: MyMap1[str, int], m2: MyMap2[int, str]):
154 assert_type(m1["key"], int)
155 assert_type(m2["key"], int)
157 m1[0] # E
[invalid-argument-type] Method `__getitem__` of type `bound method MyMap1[str, int].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap1[str, int]`
158 m2[0] # E
[invalid-argument-type] Method `__getitem__` of type `bound method MyMap2[int, str].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap2[int, str]`
160# > All arguments to ``Generic`` or ``Protocol`` must be type variables.
162class Bad1(Generic[int]): ... # E
[invalid-argument-type] `<class 'int'>` is not a valid argument to `Generic`
163class Bad2(Protocol[int]): ... # E
[invalid-argument-type] `<class 'int'>` is not a valid argument to `Protocol`
165# > All type parameters for the class must appear within the ``Generic`` or
166# > ``Protocol`` type argument list.
168T_co = TypeVar("T_co", covariant=True)
169S_co = TypeVar("S_co", covariant=True)
171class Bad3(Iterable[T_co], Generic[S_co]): ... # E
[invalid-generic-class] `Generic` base class must include all type variables used in other base classes
172class Bad4(Iterable[T_co], Protocol[S_co]): ... # E
[invalid-generic-class] `Generic` base class must include all type variables used in other base classes
174# > The above rule does not apply to a bare ``Protocol`` base class.
176class MyIterator(Iterator[T_co], Protocol): ... # OK
178# > You can use multiple inheritance with ``Generic``
180from collections.abc import Sized, Container
183class LinkedList(Sized, Generic[T]): ...
186class MyMapping(Iterable[tuple[K, V]], Container[tuple[K, V]], Generic[K, V]): ...
189# > Subclassing a generic class without specifying type parameters assumes
190# > ``Any`` for each position. In the following example, ``MyIterable``
191# > is not generic but implicitly inherits from ``Iterable[Any]``::
194class MyIterableAny(Iterable): # Same as Iterable[Any]
195 ...
198def test_my_iterable_any(m: MyIterableAny):
199 assert_type(iter(m), Iterator[Any])
202# > Generic metaclasses are not supported
205class GenericMeta(type, Generic[T]): ...
208class GenericMetaInstance(metaclass=GenericMeta[T]): # E
Expected a ty diagnostic for this line
209 ...