Skip to content

Commit

Permalink
Feedback, add conformance test
Browse files Browse the repository at this point in the history
  • Loading branch information
Viicos committed Feb 24, 2025
1 parent d2d8036 commit 97334a2
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 72 deletions.
29 changes: 13 additions & 16 deletions conformance/results/mypy/classes_classvar.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ Internal error if TypeVarTuple is used in ClassVar.
Does not reject use of ParamSpec in ClassVar.
Rejects ClassVar nested in Annotated.
Does not reject use of ClassVar in TypeAlias definition.
Does not infer type of ClassVar from assignment if no type is provided.
"""
output = """
classes_classvar.py:38: error: ClassVar[...] must have at most one type argument [valid-type]
Expand All @@ -15,24 +14,22 @@ classes_classvar.py:46: error: ClassVar cannot contain type variables [misc]
classes_classvar.py:52: error: Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "list[str]") [assignment]
classes_classvar.py:54: error: Variable should not be annotated with both ClassVar and Final [misc]
classes_classvar.py:55: error: Invalid type: ClassVar nested inside other type [valid-type]
classes_classvar.py:61: error: Invalid type: ClassVar nested inside other type [valid-type]
classes_classvar.py:63: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:64: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:65: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:67: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:67: error: Invalid type: ClassVar nested inside other type [valid-type]
classes_classvar.py:69: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:70: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:71: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:78: error: Expression is of type "Any", not "float" [assert-type]
classes_classvar.py:105: error: Cannot assign to class variable "stats" via instance [misc]
classes_classvar.py:134: error: Incompatible types in assignment (expression has type "ProtoAImpl", variable has type "ProtoA") [assignment]
classes_classvar.py:134: note: "ProtoAImpl" is missing following "ProtoA" protocol member:
classes_classvar.py:134: note: z
classes_classvar.py:134: note: Protocol member ProtoA.x expected class variable, got instance variable
classes_classvar.py:134: note: Protocol member ProtoA.y expected class variable, got instance variable
classes_classvar.py:73: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:77: error: ClassVar can only be used for assignments in class body [misc]
classes_classvar.py:111: error: Cannot assign to class variable "stats" via instance [misc]
classes_classvar.py:140: error: Incompatible types in assignment (expression has type "ProtoAImpl", variable has type "ProtoA") [assignment]
classes_classvar.py:140: note: "ProtoAImpl" is missing following "ProtoA" protocol member:
classes_classvar.py:140: note: z
classes_classvar.py:140: note: Protocol member ProtoA.x expected class variable, got instance variable
classes_classvar.py:140: note: Protocol member ProtoA.y expected class variable, got instance variable
"""
conformance_automated = "Fail"
errors_diff = """
Line 47: Expected 1 errors
Line 72: Expected 1 errors
Line 61: Unexpected errors ['classes_classvar.py:61: error: Invalid type: ClassVar nested inside other type [valid-type]']
Line 78: Unexpected errors ['classes_classvar.py:78: error: Expression is of type "Any", not "float" [assert-type]']
Line 78: Expected 1 errors
Line 67: Unexpected errors ['classes_classvar.py:67: error: Invalid type: ClassVar nested inside other type [valid-type]']
"""
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.15.0"
test_duration = 1.8
test_duration = 2.1
25 changes: 13 additions & 12 deletions conformance/results/pyre/classes_classvar.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ notes = """
Does not reject use of TypeVar in ClassVar.
Does not reject use of ParamSpec in ClassVar.
Does not reject use of ClassVar as a generic type argument.
Does not allow ClassVar to be used without an assigned value.
Does not reject use of ClassVar in parameter type annotation.
Does not reject use of ClassVar in local variable annotation.
Does not reject use of ClassVar in instance variable annotation.
Expand All @@ -15,11 +16,11 @@ classes_classvar.py:38:10 Invalid type parameters [24]: Generic type `CV` expect
classes_classvar.py:39:10 Invalid type [31]: Expression `typing.ClassVar[3]` is not a valid type.
classes_classvar.py:40:13 Unbound name [10]: Name `var` is used but not defined in the current scope.
classes_classvar.py:52:4 Incompatible attribute type [8]: Attribute `bad8` declared in class `ClassA` has type `List[str]` but is used as type `Dict[Variable[_KT], Variable[_VT]]`.
classes_classvar.py:65:8 Undefined attribute [16]: `ClassA` has no attribute `xx`.
classes_classvar.py:68:8 Incompatible return type [7]: Expected `CV[int]` but got `int`.
classes_classvar.py:78:0 Assert type [70]: Expected `float` but got `typing.Any`.
classes_classvar.py:105:0 Invalid assignment [41]: Assigning to class variable through instance, did you mean to assign to `Starship.stats` instead?
classes_classvar.py:134:0 Incompatible variable type [9]: a is declared to have type `ProtoA` but is used as type `ProtoAImpl`.
classes_classvar.py:66:4 Uninitialized attribute [13]: Attribute `good5` is declared in class `ClassA` to have type `typing.Any` but is never initialized.
classes_classvar.py:71:8 Undefined attribute [16]: `ClassA` has no attribute `xx`.
classes_classvar.py:74:8 Incompatible return type [7]: Expected `CV[int]` but got `int`.
classes_classvar.py:111:0 Invalid assignment [41]: Assigning to class variable through instance, did you mean to assign to `Starship.stats` instead?
classes_classvar.py:140:0 Incompatible variable type [9]: a is declared to have type `ProtoA` but is used as type `ProtoAImpl`.
"""
conformance_automated = "Fail"
errors_diff = """
Expand All @@ -28,11 +29,11 @@ Line 46: Expected 1 errors
Line 47: Expected 1 errors
Line 54: Expected 1 errors
Line 55: Expected 1 errors
Line 63: Expected 1 errors
Line 64: Expected 1 errors
Line 67: Expected 1 errors
Line 71: Expected 1 errors
Line 72: Expected 1 errors
Line 68: Unexpected errors ['classes_classvar.py:68:8 Incompatible return type [7]: Expected `CV[int]` but got `int`.']
Line 78: Unexpected errors ['classes_classvar.py:78:0 Assert type [70]: Expected `float` but got `typing.Any`.']
Line 69: Expected 1 errors
Line 70: Expected 1 errors
Line 73: Expected 1 errors
Line 77: Expected 1 errors
Line 78: Expected 1 errors
Line 66: Unexpected errors ['classes_classvar.py:66:4 Uninitialized attribute [13]: Attribute `good5` is declared in class `ClassA` to have type `typing.Any` but is never initialized.']
Line 74: Unexpected errors ['classes_classvar.py:74:8 Incompatible return type [7]: Expected `CV[int]` but got `int`.']
"""
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.23"
test_duration = 6.5
test_duration = 3.9
16 changes: 8 additions & 8 deletions conformance/results/pyright/classes_classvar.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ classes_classvar.py:47:20 - error: "ClassVar" type cannot include type variables
classes_classvar.py:52:33 - error: Type "dict[Any, Any]" is not assignable to declared type "list[str]" (reportAssignmentType)
classes_classvar.py:54:17 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:55:17 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:63:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:64:12 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:65:18 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:67:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:71:8 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:72:20 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:105:14 - error: Cannot assign to attribute "stats" for class "Starship"
classes_classvar.py:69:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:70:12 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:71:18 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:73:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:77:8 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:78:20 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm)
classes_classvar.py:111:14 - error: Cannot assign to attribute "stats" for class "Starship"
  Attribute "stats" cannot be assigned through a class instance because it is a ClassVar (reportAttributeAccessIssue)
classes_classvar.py:134:13 - error: Type "ProtoAImpl" is not assignable to declared type "ProtoA"
classes_classvar.py:140:13 - error: Type "ProtoAImpl" is not assignable to declared type "ProtoA"
  "ProtoAImpl" is incompatible with protocol "ProtoA"
    "x" is defined as a ClassVar in protocol
    "y" is defined as a ClassVar in protocol (reportAssignmentType)
Expand Down
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.394"
test_duration = 1.3
test_duration = 2.3
35 changes: 18 additions & 17 deletions conformance/results/pytype/classes_classvar.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Does not reject use of TypeVar in ClassVar.
Does not reject use of ParamSpec in ClassVar.
Does not reject use of ClassVar as a generic type argument.
Rejects initialization of ClassVar if no type argument is provided.
Does not infer ClassVar with no type argument and no assigned value as Any.
Does not reject use of ClassVar in parameter type annotation.
Does not reject use of ClassVar in local variable annotation.
Does not reject use of ClassVar in instance variable annotation.
Expand Down Expand Up @@ -85,27 +86,27 @@ classes_classvar.py:52:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ClassA
bad8: ClassVar[list[str]] = {} # E: type violation in initialization
\u001b[1m\u001b[31m~~~~\u001b[39m\u001b[0m
classes_classvar.py:60:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ClassA: Type annotation for good4 does not match type of assignment [annotation-type-mismatch]
classes_classvar.py:63:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ClassA: Type annotation for good4 does not match type of assignment [annotation-type-mismatch]
good4: ClassVar = 3.1
\u001b[1m\u001b[31m~~~~~\u001b[39m\u001b[0m
classes_classvar.py:68:16: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in method2: bad return type [bad-return-type]
classes_classvar.py:74:16: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in method2: bad return type [bad-return-type]
return 3
\u001b[1m\u001b[31m~\u001b[39m\u001b[0m
classes_classvar.py:78:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in <module>: ClassVar [assert-type]
classes_classvar.py:84:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in <module>: ClassVar [assert-type]
assert_type(ClassA.good4, float)
\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m
assert_type(ClassA.good5, Any)
\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m
classes_classvar.py:124:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ProtoA: Type annotation for z does not match type of assignment [annotation-type-mismatch]
classes_classvar.py:130:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ProtoA: Type annotation for z does not match type of assignment [annotation-type-mismatch]
z: CV = [""]
\u001b[1m\u001b[31m~\u001b[39m\u001b[0m
classes_classvar.py:134:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in <module>: Type annotation for a does not match type of assignment [annotation-type-mismatch]
classes_classvar.py:140:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in <module>: Type annotation for a does not match type of assignment [annotation-type-mismatch]
a: ProtoA = ProtoAImpl() # E: y is not a ClassVar
\u001b[1m\u001b[31m~\u001b[39m\u001b[0m
Expand All @@ -118,17 +119,17 @@ Line 46: Expected 1 errors
Line 47: Expected 1 errors
Line 54: Expected 1 errors
Line 55: Expected 1 errors
Line 63: Expected 1 errors
Line 64: Expected 1 errors
Line 65: Expected 1 errors
Line 67: Expected 1 errors
Line 69: Expected 1 errors
Line 70: Expected 1 errors
Line 71: Expected 1 errors
Line 72: Expected 1 errors
Line 105: Expected 1 errors
Line 73: Expected 1 errors
Line 77: Expected 1 errors
Line 78: Expected 1 errors
Line 111: Expected 1 errors
Line 7: Unexpected errors ['classes_classvar.py:7:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in <module>: typing.TypeVarTuple not supported yet [not-supported-yet]']
Line 29: Unexpected errors ['classes_classvar.py:29:6: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in <module>: Function TypeVarTuple.__init__ expects 1 arg(s), got 2 [wrong-arg-count]']
Line 60: Unexpected errors ['classes_classvar.py:60:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in ClassA: Type annotation for good4 does not match type of assignment [annotation-type-mismatch]']
Line 68: Unexpected errors ['classes_classvar.py:68:16: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in method2: bad return type [bad-return-type]']
Line 78: Unexpected errors ['classes_classvar.py:78:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in <module>: ClassVar [assert-type]']
Line 124: Unexpected errors ['classes_classvar.py:124:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in ProtoA: Type annotation for z does not match type of assignment [annotation-type-mismatch]']
Line 63: Unexpected errors ['classes_classvar.py:63:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in ClassA: Type annotation for good4 does not match type of assignment [annotation-type-mismatch]']
Line 74: Unexpected errors ['classes_classvar.py:74:16: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in method2: bad return type [bad-return-type]']
Line 84: Unexpected errors ['classes_classvar.py:84:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in <module>: ClassVar [assert-type]']
Line 130: Unexpected errors ['classes_classvar.py:130:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in ProtoA: Type annotation for z does not match type of assignment [annotation-type-mismatch]']
"""
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.10.11"
test_duration = 34.9
test_duration = 43.3
8 changes: 4 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.15.0</div>
<div class='tc-time'>1.8sec</div>
<div class='tc-time'>2.1sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pyright 1.1.394</div>
<div class='tc-time'>1.3sec</div>
<div class='tc-time'>2.3sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pyre 0.9.23</div>
<div class='tc-time'>6.5sec</div>
<div class='tc-time'>3.9sec</div>
</th>
<th class='tc-header'><div class='tc-name'>pytype 2024.10.11</div>
<div class='tc-time'>34.9sec</div>
<div class='tc-time'>43.3sec</div>
</th>
</tr>
<tr><th class="column" colspan="5">
Expand Down
10 changes: 8 additions & 2 deletions conformance/tests/classes_classvar.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ class ClassA(Generic[T, P]):
good1: CV[int] = 1
good2: ClassVar[list[str]] = []
good3: ClassVar[Any] = 1
# > If an assigned value is available, the type should be inferred as some type
# > to which this value is assignable.
# Here, type checkers could infer good4 as `float` or `Any`, for example.
good4: ClassVar = 3.1
good5: Annotated[ClassVar[list[int]], ""] = []
# > If the `ClassVar` qualifier is used without any assigned value, the type
# > should be inferred as `Any`:
good5: ClassVar
good6: Annotated[ClassVar[list[int]], ""] = []

def method1(self, a: ClassVar[int]): # E: ClassVar not allowed here
x: ClassVar[str] = "" # E: ClassVar not allowed here
Expand All @@ -75,7 +81,7 @@ def method2(self) -> ClassVar[int]: # E: ClassVar not allowed here
assert_type(ClassA.good1, int)
assert_type(ClassA.good2, list[str])
assert_type(ClassA.good3, Any)
assert_type(ClassA.good4, float)
assert_type(ClassA.good5, Any)


class BasicStarship:
Expand Down
12 changes: 3 additions & 9 deletions docs/spec/class-compat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ The :py:data:`typing.ClassVar` :term:`type qualifier` is used to annotate
class variables that should not be set on class instances. This restriction
is enforced by static checkers, but not at runtime.

Syntax
^^^^^^

:py:data:`~typing.ClassVar` may be used in one of several forms:

* With an explicit type, using the syntax ``ClassVar[<type>]``. Example::
Expand All @@ -31,14 +28,11 @@ Syntax
z: ClassVar

If an assigned value is available (e.g. with ``y``), the type should be
inferred as some type to which this value is :term:`assignable` (in this
case, either ``int``, ``Literal[2]``, or ``Any``).
inferred as some type to which this value is :term:`assignable` (for example,
``int``, ``Literal[2]``, or ``Any``).

If the ``ClassVar`` qualifier is used without any assigned value, the type
should be inferred to an unknown static type (such as :ref:`Any`).

Semantics and examples
^^^^^^^^^^^^^^^^^^^^^^
should be inferred as :ref:`any`.

Type annotations can be used to annotate class and instance variables
in class bodies and methods. In particular, the value-less notation ``a: int``
Expand Down

0 comments on commit 97334a2

Please sign in to comment.