Skip to content

Commit 1b141ff

Browse files
authored
Add conformance test for generic types as base classes (#1590)
1 parent 3c02f03 commit 1b141ff

File tree

5 files changed

+81
-0
lines changed

5 files changed

+81
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
conformant = "Pass"
2+
output = """
3+
generics_base_class.py:22: error: Argument 1 to "takes_dict_incorrect" has incompatible type "SymbolTable"; expected "dict[str, list[object]]" [arg-type]
4+
generics_base_class.py:37: error: "LinkedList" expects 1 type argument, but 2 given [type-arg]
5+
generics_base_class.py:46: error: "MyDict" expects 1 type argument, but 2 given [type-arg]
6+
"""
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
conformant = "Pass"
2+
notes = """
3+
Doesn't allow using generic in assert_type expression.
4+
"""
5+
output = """
6+
generics_base_class.py:22:25 Incompatible parameter type [6]: In call `takes_dict_incorrect`, for 1st positional argument, expected `Dict[str, List[object]]` but got `SymbolTable`.
7+
generics_base_class.py:34:25 Undefined attribute [16]: `typing.Iterator` has no attribute `__getitem__`.
8+
generics_base_class.py:37:21 Invalid type parameters [24]: Generic type `LinkedList` expects 1 type parameter, received 2.
9+
generics_base_class.py:46:17 Invalid type parameters [24]: Generic type `MyDict` expects 1 type parameter, received 2.
10+
"""
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
conformant = "Pass"
2+
output = """
3+
generics_base_class.py:22:26 - error: Argument of type "SymbolTable" cannot be assigned to parameter "x" of type "dict[str, list[object]]" in function "takes_dict_incorrect"
4+
  "SymbolTable" is incompatible with "dict[str, list[object]]"
5+
    Type parameter "_VT@dict" is invariant, but "list[Node]" is not the same as "list[object]"
6+
    Consider switching from "dict" to "Mapping" which is covariant in the value type (reportGeneralTypeIssues)
7+
generics_base_class.py:37:38 - error: Too many type arguments provided for "LinkedList"; expected 1 but received 2 (reportGeneralTypeIssues)
8+
generics_base_class.py:46:30 - error: Too many type arguments provided for "MyDict"; expected 1 but received 2 (reportGeneralTypeIssues)
9+
"""
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
conformant = "Partial"
2+
notes = """
3+
False negative on passing SymbolTable to dict[str, list[object]].
4+
"""
5+
output = """
6+
File "generics_base_class.py", line 37, in <module>: Invalid type annotation 'LinkedList[int, int]' [invalid-annotation]
7+
LinkedList[T] expected 1 parameter, got 2
8+
File "generics_base_class.py", line 46, in <module>: Invalid type annotation 'MyDict[int, int]' [invalid-annotation]
9+
MyDict[T] expected 1 parameter, got 2
10+
"""
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#arbitrary-generic-types-as-base-classes
2+
3+
from typing import TypeVar, Iterable, assert_type
4+
5+
T = TypeVar("T")
6+
7+
# > Generic[T] is only valid as a base class – it’s not a proper type. However,
8+
# > user-defined generic types [...] and built-in generic types and ABCs such as
9+
# > list[T] and Iterable[T] are valid both as types and as base classes.
10+
11+
class Node: ...
12+
13+
class SymbolTable(dict[str, list[Node]]): ...
14+
15+
def takes_dict(x: dict): ...
16+
def takes_dict_typed(x: dict[str, list[Node]]): ...
17+
def takes_dict_incorrect(x: dict[str, list[object]]): ...
18+
19+
def test_symbol_table(s: SymbolTable):
20+
takes_dict(s) # OK
21+
takes_dict_typed(s) # OK
22+
takes_dict_incorrect(s) # Type error
23+
24+
# > If a generic base class has a type variable as a type argument, this makes
25+
# > the defined class generic.
26+
27+
# Note that there is overlap in the spec and tests in generics_basic.py
28+
29+
from collections.abc import Iterable, Container, Iterator
30+
31+
class LinkedList(Iterable[T], Container[T]): ...
32+
33+
def test_linked_list(l: LinkedList[int]):
34+
assert_type(iter(l), Iterator[int])
35+
assert_type(l.__contains__(1), bool)
36+
37+
linked_list_invalid: LinkedList[int, int] # Type error
38+
39+
from collections.abc import Mapping
40+
41+
class MyDict(Mapping[str, T]): ...
42+
43+
def test_my_dict(d: MyDict[int]):
44+
assert_type(d["a"], int)
45+
46+
my_dict_invalid: MyDict[int, int] # Type error

0 commit comments

Comments
 (0)