Skip to content

Commit 2d2e8ee

Browse files
authored
Add conformance tests for Final dataclass fields (#1741)
1 parent ee2f893 commit 2d2e8ee

File tree

11 files changed

+129
-9
lines changed

11 files changed

+129
-9
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
conformant = "Partial"
2+
notes = """
3+
Wrongly requires a Final dataclass field to be initialized at class level.
4+
Doesn't support Final nested inside ClassVar.
5+
"""
6+
conformance_automated = "Fail"
7+
errors_diff = """
8+
Line 16: Expected 1 errors
9+
Line 6: Unexpected errors ['dataclasses_final.py:6: error: Final name must be initialized with a value [misc]']
10+
Line 8: Unexpected errors ['dataclasses_final.py:8: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]']
11+
Line 13: Unexpected errors ['dataclasses_final.py:13: error: Expression is of type "Any", not "int" [assert-type]']
12+
"""
13+
output = """
14+
dataclasses_final.py:6: error: Final name must be initialized with a value [misc]
15+
dataclasses_final.py:8: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]
16+
dataclasses_final.py:13: error: Expression is of type "Any", not "int" [assert-type]
17+
dataclasses_final.py:24: error: Cannot assign to final attribute "final_no_default" [misc]
18+
dataclasses_final.py:25: error: Cannot assign to final attribute "final_with_default" [misc]
19+
dataclasses_final.py:26: error: Cannot access final instance attribute "final_no_default" on class object [misc]
20+
dataclasses_final.py:26: error: Cannot assign to final attribute "final_no_default" [misc]
21+
dataclasses_final.py:27: error: Cannot assign to final attribute "final_with_default" [misc]
22+
"""

conformance/results/mypy/version.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "mypy 1.10.0"
2-
test_duration = 1.4
2+
test_duration = 1.0
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
conformant = "Partial"
2+
notes = """
3+
Mis-handles Final nested inside ClassVar.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 8: Unexpected errors ['dataclasses_final.py:8:4 Incompatible attribute type [8]: Attribute `final_classvar` declared in class `D` has type `Final[int]` but is used as type `int`.', 'dataclasses_final.py:8:4 Invalid type [31]: Expression `Final[int]` is not a valid type. Final cannot be nested.']
8+
Line 13: Unexpected errors ['dataclasses_final.py:13:0 Incompatible parameter type [6]: In call `assert_type`, for 1st positional argument, expected `int` but got `Final[int]`.']
9+
"""
10+
output = """
11+
dataclasses_final.py:8:4 Incompatible attribute type [8]: Attribute `final_classvar` declared in class `D` has type `Final[int]` but is used as type `int`.
12+
dataclasses_final.py:8:4 Invalid type [31]: Expression `Final[int]` is not a valid type. Final cannot be nested.
13+
dataclasses_final.py:13:0 Incompatible parameter type [6]: In call `assert_type`, for 1st positional argument, expected `int` but got `Final[int]`.
14+
dataclasses_final.py:16:0 Incompatible attribute type [8]: Attribute `final_classvar` declared in class `D` has type `Final[int]` but is used as type `int`.
15+
dataclasses_final.py:24:0 Invalid assignment [41]: Cannot reassign final attribute `d.final_no_default`.
16+
dataclasses_final.py:25:0 Invalid assignment [41]: Cannot reassign final attribute `d.final_with_default`.
17+
dataclasses_final.py:26:0 Invalid assignment [41]: Cannot reassign final attribute `D.final_no_default`.
18+
dataclasses_final.py:27:0 Invalid assignment [41]: Cannot reassign final attribute `D.final_with_default`.
19+
"""

conformance/results/pyre/version.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pyre 0.9.21"
2-
test_duration = 3.2
2+
test_duration = 1.9
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
conformant = "Partial"
2+
notes = """
3+
Doesn't support Final nested inside ClassVar.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 8: Unexpected errors ['dataclasses_final.py:8:30 - error: "Final" is not allowed in this context', 'dataclasses_final.py:8:44 - error: Expression of type "Literal[4]" is incompatible with declared type "Final"']
8+
Line 13: Unexpected errors ['dataclasses_final.py:13:13 - error: "assert_type" mismatch: expected "int" but received "Final" (reportAssertTypeFailure)']
9+
"""
10+
output = """
11+
dataclasses_final.py:8:30 - error: "Final" is not allowed in this context
12+
dataclasses_final.py:8:44 - error: Expression of type "Literal[4]" is incompatible with declared type "Final"
13+
  "Literal[4]" is incompatible with "Final" (reportAssignmentType)
14+
dataclasses_final.py:13:13 - error: "assert_type" mismatch: expected "int" but received "Final" (reportAssertTypeFailure)
15+
dataclasses_final.py:16:20 - error: Cannot assign to attribute "final_classvar" for class "type[D]"
16+
  "Literal[10]" is incompatible with "Final" (reportAttributeAccessIssue)
17+
dataclasses_final.py:24:3 - error: Cannot assign to attribute "final_no_default" for class "D"
18+
  "final_no_default" is declared as Final and cannot be reassigned
19+
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
20+
dataclasses_final.py:25:3 - error: Cannot assign to attribute "final_with_default" for class "D"
21+
  "final_with_default" is declared as Final and cannot be reassigned
22+
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
23+
dataclasses_final.py:26:3 - error: Cannot assign to attribute "final_no_default" for class "type[D]"
24+
  "final_no_default" is declared as Final and cannot be reassigned
25+
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
26+
dataclasses_final.py:27:3 - error: Cannot assign to attribute "final_with_default" for class "type[D]"
27+
  "final_with_default" is declared as Final and cannot be reassigned
28+
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
29+
"""
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pyright 1.1.363"
2-
test_duration = 1.4
2+
test_duration = 1.7
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
conformant = "Partial"
2+
notes = """
3+
Doesn't handle Final nested inside ClassVar.
4+
"""
5+
errors_diff = """
6+
Line 16: Expected 1 errors
7+
Line 24: Expected 1 errors
8+
Line 26: Expected 1 errors
9+
Line 8: Unexpected errors ['File "dataclasses_final.py", line 8, in D: Invalid use of typing.Final [final-error]', 'File "dataclasses_final.py", line 8, in D: Invalid type annotation \\'ClassVar[Final[int]]\\' [invalid-annotation]']
10+
"""
11+
output = """
12+
File "dataclasses_final.py", line 8, in D: Invalid use of typing.Final [final-error]
13+
File "dataclasses_final.py", line 8, in D: Invalid type annotation 'ClassVar[Final[int]]' [invalid-annotation]
14+
File "dataclasses_final.py", line 25, in <module>: Assigning to attribute final_with_default, which was annotated with Final [final-error]
15+
File "dataclasses_final.py", line 27, in <module>: Assigning to attribute final_with_default, which was annotated with Final [final-error]
16+
"""
17+
conformance_automated = "Fail"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pytype 2024.04.11"
2-
test_duration = 30.6
2+
test_duration = 28.3

conformance/results/results.html

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,16 @@ <h3>Python Type System Conformance Test Results</h3>
159159
<div class="table_container"><table><tbody>
160160
<tr><th class="col1">&nbsp;</th>
161161
<th class='tc-header'><div class='tc-name'>mypy 1.10.0</div>
162-
<div class='tc-time'>1.4sec</div>
162+
<div class='tc-time'>1.0sec</div>
163163
</th>
164164
<th class='tc-header'><div class='tc-name'>pyright 1.1.363</div>
165-
<div class='tc-time'>1.4sec</div>
165+
<div class='tc-time'>1.7sec</div>
166166
</th>
167167
<th class='tc-header'><div class='tc-name'>pyre 0.9.21</div>
168-
<div class='tc-time'>3.2sec</div>
168+
<div class='tc-time'>1.9sec</div>
169169
</th>
170170
<th class='tc-header'><div class='tc-name'>pytype 2024.04.11</div>
171-
<div class='tc-time'>30.6sec</div>
171+
<div class='tc-time'>28.3sec</div>
172172
</th>
173173
</tr>
174174
<tr><th class="column" colspan="5">
@@ -690,6 +690,12 @@ <h3>Python Type System Conformance Test Results</h3>
690690
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Incorrectly generates error when calling constructor of dataclass with descriptor.</p></span></div></th>
691691
<th class="column col2 not-conformant"><div class="hover-text">Unsupported<span class="tooltip-text" id="bottom"><p>Does not understand descriptor objects in dataclass.</p></span></div></th>
692692
</tr>
693+
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataclasses_final</th>
694+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Wrongly requires a Final dataclass field to be initialized at class level.</p><p>Doesn't support Final nested inside ClassVar.</p></span></div></th>
695+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Doesn't support Final nested inside ClassVar.</p></span></div></th>
696+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Mis-handles Final nested inside ClassVar.</p></span></div></th>
697+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Doesn't handle Final nested inside ClassVar.</p></span></div></th>
698+
</tr>
693699
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataclasses_frozen</th>
694700
<th class="column col2 conformant">Pass</th>
695701
<th class="column col2 conformant">Pass</th>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from dataclasses import dataclass
2+
from typing import assert_type, ClassVar, Final
3+
4+
@dataclass
5+
class D:
6+
final_no_default: Final[int]
7+
final_with_default: Final[str] = "foo"
8+
final_classvar: ClassVar[Final[int]] = 4
9+
# we don't require support for Final[ClassVar[...]] because the dataclasses
10+
# runtime implementation won't recognize it as a ClassVar either
11+
12+
# An explicitly marked ClassVar can be accessed on the class:
13+
assert_type(D.final_classvar, int)
14+
15+
# ...but not assigned to, because it's Final:
16+
D.final_classvar = 10 # E: can't assign to final attribute
17+
18+
# A non-ClassVar attribute (with or without default) is a dataclass field:
19+
d = D(final_no_default=1, final_with_default="bar")
20+
assert_type(d.final_no_default, int)
21+
assert_type(d.final_with_default, str)
22+
23+
# ... but can't be assigned to (on the class or on an instance):
24+
d.final_no_default = 10 # E: can't assign to final attribute
25+
d.final_with_default = "baz" # E: can't assign to final attribute
26+
D.final_no_default = 10 # E: can't assign to final attribute / can't assign instance attr on class
27+
D.final_with_default = "baz" # E: can't assign to final attribute / can't assign instance attr on class

conformance/tests/qualifiers_final_annotation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class ClassCChild(ClassC):
9898

9999
# > Type checkers should infer a final attribute that is initialized in a class
100100
# > body as being a class variable. Variables should not be annotated with both
101-
# > ClassVar and Final.
101+
# > ClassVar and Final. (Except in a dataclass; see dataclasses_final.py.)
102102

103103

104104
class ClassD:

0 commit comments

Comments
 (0)