Skip to content

Commit

Permalink
Add conformance test for generic types as base classes (#1590)
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja authored Jan 16, 2024
1 parent 3c02f03 commit 1b141ff
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 0 deletions.
6 changes: 6 additions & 0 deletions conformance/results/mypy/generics_base_class.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
conformant = "Pass"
output = """
generics_base_class.py:22: error: Argument 1 to "takes_dict_incorrect" has incompatible type "SymbolTable"; expected "dict[str, list[object]]" [arg-type]
generics_base_class.py:37: error: "LinkedList" expects 1 type argument, but 2 given [type-arg]
generics_base_class.py:46: error: "MyDict" expects 1 type argument, but 2 given [type-arg]
"""
10 changes: 10 additions & 0 deletions conformance/results/pyre/generics_base_class.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
conformant = "Pass"
notes = """
Doesn't allow using generic in assert_type expression.
"""
output = """
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`.
generics_base_class.py:34:25 Undefined attribute [16]: `typing.Iterator` has no attribute `__getitem__`.
generics_base_class.py:37:21 Invalid type parameters [24]: Generic type `LinkedList` expects 1 type parameter, received 2.
generics_base_class.py:46:17 Invalid type parameters [24]: Generic type `MyDict` expects 1 type parameter, received 2.
"""
9 changes: 9 additions & 0 deletions conformance/results/pyright/generics_base_class.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
conformant = "Pass"
output = """
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"
  "SymbolTable" is incompatible with "dict[str, list[object]]"
    Type parameter "_VT@dict" is invariant, but "list[Node]" is not the same as "list[object]"
    Consider switching from "dict" to "Mapping" which is covariant in the value type (reportGeneralTypeIssues)
generics_base_class.py:37:38 - error: Too many type arguments provided for "LinkedList"; expected 1 but received 2 (reportGeneralTypeIssues)
generics_base_class.py:46:30 - error: Too many type arguments provided for "MyDict"; expected 1 but received 2 (reportGeneralTypeIssues)
"""
10 changes: 10 additions & 0 deletions conformance/results/pytype/generics_base_class.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
conformant = "Partial"
notes = """
False negative on passing SymbolTable to dict[str, list[object]].
"""
output = """
File "generics_base_class.py", line 37, in <module>: Invalid type annotation 'LinkedList[int, int]' [invalid-annotation]
LinkedList[T] expected 1 parameter, got 2
File "generics_base_class.py", line 46, in <module>: Invalid type annotation 'MyDict[int, int]' [invalid-annotation]
MyDict[T] expected 1 parameter, got 2
"""
46 changes: 46 additions & 0 deletions conformance/tests/generics_base_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#arbitrary-generic-types-as-base-classes

from typing import TypeVar, Iterable, assert_type

T = TypeVar("T")

# > Generic[T] is only valid as a base class – it’s not a proper type. However,
# > user-defined generic types [...] and built-in generic types and ABCs such as
# > list[T] and Iterable[T] are valid both as types and as base classes.

class Node: ...

class SymbolTable(dict[str, list[Node]]): ...

def takes_dict(x: dict): ...
def takes_dict_typed(x: dict[str, list[Node]]): ...
def takes_dict_incorrect(x: dict[str, list[object]]): ...

def test_symbol_table(s: SymbolTable):
takes_dict(s) # OK
takes_dict_typed(s) # OK
takes_dict_incorrect(s) # Type error

# > If a generic base class has a type variable as a type argument, this makes
# > the defined class generic.

# Note that there is overlap in the spec and tests in generics_basic.py

from collections.abc import Iterable, Container, Iterator

class LinkedList(Iterable[T], Container[T]): ...

def test_linked_list(l: LinkedList[int]):
assert_type(iter(l), Iterator[int])
assert_type(l.__contains__(1), bool)

linked_list_invalid: LinkedList[int, int] # Type error

from collections.abc import Mapping

class MyDict(Mapping[str, T]): ...

def test_my_dict(d: MyDict[int]):
assert_type(d["a"], int)

my_dict_invalid: MyDict[int, int] # Type error

0 comments on commit 1b141ff

Please sign in to comment.