Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8279fa6

Browse filesBrowse files
authored
Conformance tests and spec change for generic type erasure (#1589)
1 parent ebb1a42 commit 8279fa6
Copy full SHA for 8279fa6

File tree

Expand file treeCollapse file tree

6 files changed

+106
-4
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+106
-4
lines changed
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
conformant = "Partial"
2+
notes = """
3+
Infers Node[Never] instead of Node[Any] when argument is not provided.
4+
False negative on instance attribute access on type(node).
5+
"""
6+
output = """
7+
generics_type_erasure.py:17: error: Expression is of type "Node[Never]", not "Node[Any]" [assert-type]
8+
generics_type_erasure.py:20: error: Expression is of type Never, not "Any" [assert-type]
9+
generics_type_erasure.py:36: error: Argument 1 to "Node" has incompatible type "str"; expected "int | None" [arg-type]
10+
generics_type_erasure.py:38: error: Argument 1 to "Node" has incompatible type "int"; expected "str | None" [arg-type]
11+
generics_type_erasure.py:40: error: Access to generic instance variables via class is ambiguous [misc]
12+
generics_type_erasure.py:41: error: Access to generic instance variables via class is ambiguous [misc]
13+
generics_type_erasure.py:42: error: Access to generic instance variables via class is ambiguous [misc]
14+
generics_type_erasure.py:43: error: Access to generic instance variables via class is ambiguous [misc]
15+
"""
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
conformant = "Partial"
2+
notes = """
3+
Doesn't allow using Node[Any] in assert_type expression.
4+
False negatives on instance attribute access on the type.
5+
"""
6+
output = """
7+
generics_type_erasure.py:11:0 Uninitialized attribute [13]: Attribute `label` is declared in class `Node` to have type `Variable[T]` but is never initialized.
8+
generics_type_erasure.py:17:25 Incompatible parameter type [6]: In call `typing.GenericMeta.__getitem__`, for 1st positional argument, expected `Type[Variable[T]]` but got `object`.
9+
generics_type_erasure.py:36:15 Incompatible parameter type [6]: In call `Node.__init__`, for 1st positional argument, expected `Optional[int]` but got `str`.
10+
generics_type_erasure.py:38:15 Incompatible parameter type [6]: In call `Node.__init__`, for 1st positional argument, expected `Optional[str]` but got `int`.
11+
"""
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
conformant = "Partial"
2+
notes = """
3+
False negatives on instance attribute access on the type.
4+
"""
5+
output = """
6+
generics_type_erasure.py:36:16 - error: Argument of type "Literal['']" cannot be assigned to parameter "label" of type "int | None" in function "__init__"
7+
  Type "Literal['']" cannot be assigned to type "int | None"
8+
    "Literal['']" is incompatible with "int"
9+
    "Literal['']" is incompatible with "None" (reportGeneralTypeIssues)
10+
generics_type_erasure.py:38:16 - error: Argument of type "Literal[0]" cannot be assigned to parameter "label" of type "str | None" in function "__init__"
11+
  Type "Literal[0]" cannot be assigned to type "str | None"
12+
    "Literal[0]" is incompatible with "str"
13+
    "Literal[0]" is incompatible with "None" (reportGeneralTypeIssues)
14+
generics_type_erasure.py:42:6 - error: Cannot assign member "label" for type "type[Node[T@Node]]"
15+
  Expression of type "Literal[1]" cannot be assigned to member "label" of class "Node[T@Node]"
16+
    Member "__set__" is unknown
17+
    Type "Literal[1]" cannot be assigned to type "T@Node" (reportGeneralTypeIssues)
18+
"""
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
conformant = "Unsupported"
2+
output = """
3+
NotImplementedError: ParameterizedClass
4+
"""
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#instantiating-generic-classes-and-type-erasure
2+
3+
from typing import Any, TypeVar, Generic, assert_type
4+
5+
T = TypeVar("T")
6+
7+
# > If the constructor (__init__ or __new__) uses T in its signature, and a
8+
# > corresponding argument value is passed, the type of the corresponding
9+
# > argument(s) is substituted. Otherwise, Any is assumed.
10+
11+
class Node(Generic[T]):
12+
label: T
13+
def __init__(self, label: T | None = None) -> None: ...
14+
15+
assert_type(Node(''), Node[str])
16+
assert_type(Node(0), Node[int])
17+
assert_type(Node(), Node[Any])
18+
19+
assert_type(Node(0).label, int)
20+
assert_type(Node().label, Any)
21+
22+
# > In case the inferred type uses [Any] but the intended type is more specific,
23+
# > you can use an annotation to force the type of the variable, e.g.:
24+
25+
n1: Node[int] = Node()
26+
assert_type(n1, Node[int])
27+
n2: Node[str] = Node()
28+
assert_type(n2, Node[str])
29+
30+
n3 = Node[int]()
31+
assert_type(n3, Node[int])
32+
n4 = Node[str]()
33+
assert_type(n4, Node[str])
34+
35+
n5 = Node[int](0) # OK
36+
n6 = Node[int]("") # Type error
37+
n7 = Node[str]("") # OK
38+
n8 = Node[str](0) # Type error
39+
40+
Node[int].label = 1 # Type error
41+
Node[int].label # Type error
42+
Node.label = 1 # Type error
43+
Node.label # Type error
44+
type(n1).label # Type error
45+
assert_type(n1.label, int)
46+
assert_type(Node[int]().label, int)
47+
n1.label = 1 # OK
48+
49+
# > [...] generic versions of concrete collections can be instantiated:
50+
51+
from typing import DefaultDict
52+
53+
data = DefaultDict[int, bytes]()
54+
assert_type(data[0], bytes)

‎docs/spec/generics.rst

Copy file name to clipboardExpand all lines: docs/spec/generics.rst
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,15 +333,15 @@ argument(s) is substituted. Otherwise, ``Any`` is assumed. Example::
333333

334334
class Node(Generic[T]):
335335
x: T # Instance attribute (see below)
336-
def __init__(self, label: T = None) -> None:
336+
def __init__(self, label: T | None = None) -> None:
337337
...
338338

339339
x = Node('') # Inferred type is Node[str]
340340
y = Node(0) # Inferred type is Node[int]
341341
z = Node() # Inferred type is Node[Any]
342342

343343
In case the inferred type uses ``[Any]`` but the intended type is more
344-
specific, you can use a type comment (see below) to force the type of
344+
specific, you can use an annotation (see below) to force the type of
345345
the variable, e.g.::
346346

347347
# (continued from previous example)
@@ -373,8 +373,8 @@ class instance that does not have an instance attribute with the same name::
373373
Node.x = 1 # Error
374374
Node.x # Error
375375
type(p).x # Error
376-
p.x # Ok (evaluates to None)
377-
Node[int]().x # Ok (evaluates to None)
376+
p.x # Ok (evaluates to int)
377+
Node[int]().x # Ok (evaluates to int)
378378
p.x = 1 # Ok, but assigning to instance attribute
379379

380380
Generic versions of abstract collections like ``Mapping`` or ``Sequence``

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.