Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conformance tests for Final dataclass fields #1741

Merged
merged 2 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions conformance/results/mypy/dataclasses_final.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
conformant = "Partial"
notes = """
Wrongly requires a Final dataclass field to be initialized at class level.
Doesn't support Final nested inside ClassVar.
"""
conformance_automated = "Fail"
errors_diff = """
Line 16: Expected 1 errors
Line 6: Unexpected errors ['dataclasses_final.py:6: error: Final name must be initialized with a value [misc]']
Line 8: Unexpected errors ['dataclasses_final.py:8: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]']
Line 13: Unexpected errors ['dataclasses_final.py:13: error: Expression is of type "Any", not "int" [assert-type]']
"""
output = """
dataclasses_final.py:6: error: Final name must be initialized with a value [misc]
dataclasses_final.py:8: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]
dataclasses_final.py:13: error: Expression is of type "Any", not "int" [assert-type]
dataclasses_final.py:24: error: Cannot assign to final attribute "final_no_default" [misc]
dataclasses_final.py:25: error: Cannot assign to final attribute "final_with_default" [misc]
dataclasses_final.py:26: error: Cannot access final instance attribute "final_no_default" on class object [misc]
dataclasses_final.py:26: error: Cannot assign to final attribute "final_no_default" [misc]
dataclasses_final.py:27: error: Cannot assign to final attribute "final_with_default" [misc]
"""
2 changes: 1 addition & 1 deletion conformance/results/mypy/version.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "mypy 1.10.0"
test_duration = 1.4
test_duration = 1.0
19 changes: 19 additions & 0 deletions conformance/results/pyre/dataclasses_final.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
conformant = "Partial"
notes = """
Mis-handles Final nested inside ClassVar.
"""
conformance_automated = "Fail"
errors_diff = """
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.']
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]`.']
"""
output = """
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.
dataclasses_final.py:13:0 Incompatible parameter type [6]: In call `assert_type`, for 1st positional argument, expected `int` but got `Final[int]`.
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`.
dataclasses_final.py:24:0 Invalid assignment [41]: Cannot reassign final attribute `d.final_no_default`.
dataclasses_final.py:25:0 Invalid assignment [41]: Cannot reassign final attribute `d.final_with_default`.
dataclasses_final.py:26:0 Invalid assignment [41]: Cannot reassign final attribute `D.final_no_default`.
dataclasses_final.py:27:0 Invalid assignment [41]: Cannot reassign final attribute `D.final_with_default`.
"""
2 changes: 1 addition & 1 deletion conformance/results/pyre/version.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "pyre 0.9.21"
test_duration = 3.2
test_duration = 1.9
29 changes: 29 additions & 0 deletions conformance/results/pyright/dataclasses_final.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
conformant = "Partial"
notes = """
Doesn't support Final nested inside ClassVar.
"""
conformance_automated = "Fail"
errors_diff = """
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"']
Line 13: Unexpected errors ['dataclasses_final.py:13:13 - error: "assert_type" mismatch: expected "int" but received "Final" (reportAssertTypeFailure)']
"""
output = """
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"
  "Literal[4]" is incompatible with "Final" (reportAssignmentType)
dataclasses_final.py:13:13 - error: "assert_type" mismatch: expected "int" but received "Final" (reportAssertTypeFailure)
dataclasses_final.py:16:20 - error: Cannot assign to attribute "final_classvar" for class "type[D]"
  "Literal[10]" is incompatible with "Final" (reportAttributeAccessIssue)
dataclasses_final.py:24:3 - error: Cannot assign to attribute "final_no_default" for class "D"
  "final_no_default" is declared as Final and cannot be reassigned
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
dataclasses_final.py:25:3 - error: Cannot assign to attribute "final_with_default" for class "D"
  "final_with_default" is declared as Final and cannot be reassigned
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
dataclasses_final.py:26:3 - error: Cannot assign to attribute "final_no_default" for class "type[D]"
  "final_no_default" is declared as Final and cannot be reassigned
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
dataclasses_final.py:27:3 - error: Cannot assign to attribute "final_with_default" for class "type[D]"
  "final_with_default" is declared as Final and cannot be reassigned
    Attribute "__set__" is unknown (reportAttributeAccessIssue)
"""
2 changes: 1 addition & 1 deletion conformance/results/pyright/version.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "pyright 1.1.363"
test_duration = 1.4
test_duration = 1.7
17 changes: 17 additions & 0 deletions conformance/results/pytype/dataclasses_final.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
conformant = "Partial"
notes = """
Doesn't handle Final nested inside ClassVar.
"""
errors_diff = """
Line 16: Expected 1 errors
Line 24: Expected 1 errors
Line 26: Expected 1 errors
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]']
"""
output = """
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]
File "dataclasses_final.py", line 25, in <module>: Assigning to attribute final_with_default, which was annotated with Final [final-error]
File "dataclasses_final.py", line 27, in <module>: Assigning to attribute final_with_default, which was annotated with Final [final-error]
"""
conformance_automated = "Fail"
2 changes: 1 addition & 1 deletion conformance/results/pytype/version.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "pytype 2024.04.11"
test_duration = 30.6
test_duration = 28.3
14 changes: 10 additions & 4 deletions conformance/results/results.html
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,16 @@ <h3>Python Type System Conformance Test Results</h3>
<div class="table_container"><table><tbody>
<tr><th class="col1">&nbsp;</th>
<th class='tc-header'><div class='tc-name'>mypy 1.10.0</div>
<div class='tc-time'>1.4sec</div>
<div class='tc-time'>1.0sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pyright 1.1.363</div>
<div class='tc-time'>1.4sec</div>
<div class='tc-time'>1.7sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pyre 0.9.21</div>
<div class='tc-time'>3.2sec</div>
<div class='tc-time'>1.9sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pytype 2024.04.11</div>
<div class='tc-time'>30.6sec</div>
<div class='tc-time'>28.3sec</div>
</th>
</tr>
<tr><th class="column" colspan="5">
Expand Down Expand Up @@ -690,6 +690,12 @@ <h3>Python Type System Conformance Test Results</h3>
<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>
<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>
</tr>
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataclasses_final</th>
<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>
<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>
<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>
<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>
</tr>
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataclasses_frozen</th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 conformant">Pass</th>
Expand Down
27 changes: 27 additions & 0 deletions conformance/tests/dataclasses_final.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from dataclasses import dataclass
from typing import assert_type, ClassVar, Final

@dataclass
class D:
final_no_default: Final[int]
final_with_default: Final[str] = "foo"
final_classvar: ClassVar[Final[int]] = 4
# we don't require support for Final[ClassVar[...]] because the dataclasses
# runtime implementation won't recognize it as a ClassVar either

# An explicitly marked ClassVar can be accessed on the class:
assert_type(D.final_classvar, int)

# ...but not assigned to, because it's Final:
D.final_classvar = 10 # E: can't assign to final attribute

# A non-ClassVar attribute (with or without default) is a dataclass field:
d = D(final_no_default=1, final_with_default="bar")
assert_type(d.final_no_default, int)
assert_type(d.final_with_default, str)

# ... but can't be assigned to (on the class or on an instance):
d.final_no_default = 10 # E: can't assign to final attribute
d.final_with_default = "baz" # E: can't assign to final attribute
D.final_no_default = 10 # E: can't assign to final attribute / can't assign instance attr on class
D.final_with_default = "baz" # E: can't assign to final attribute / can't assign instance attr on class
2 changes: 1 addition & 1 deletion conformance/tests/qualifiers_final_annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class ClassCChild(ClassC):

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


class ClassD:
Expand Down