From 2d2041b1f67772d6b84090b267f00b641ff789d5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 22 May 2025 21:45:33 -0700 Subject: [PATCH] Specify that assert_type() uses equivalence --- .../results/mypy/directives_assert_type.toml | 16 +++++++------- conformance/results/mypy/version.toml | 2 +- .../results/pyre/directives_assert_type.toml | 16 +++++++------- conformance/results/pyre/version.toml | 2 +- .../pyright/aliases_typealiastype.toml | 9 +++----- .../results/pyright/callables_protocol.toml | 7 +++---- .../pyright/directives_assert_type.toml | 21 ++++++++++++------- .../generics_syntax_infer_variance.toml | 9 +++++++- .../results/pyright/protocols_definition.toml | 2 +- conformance/results/pyright/version.toml | 4 ++-- conformance/results/results.html | 8 +++---- conformance/tests/directives_assert_type.py | 1 + docs/spec/directives.rst | 4 +++- 13 files changed, 57 insertions(+), 44 deletions(-) diff --git a/conformance/results/mypy/directives_assert_type.toml b/conformance/results/mypy/directives_assert_type.toml index a8feed5ee..cf8f1459c 100644 --- a/conformance/results/mypy/directives_assert_type.toml +++ b/conformance/results/mypy/directives_assert_type.toml @@ -1,13 +1,13 @@ conformant = "Pass" output = """ -directives_assert_type.py:27: error: Expression is of type "int | str", not "int" [assert-type] -directives_assert_type.py:28: error: Expression is of type "Any", not "int" [assert-type] -directives_assert_type.py:29: error: Expression is of type "Literal[4]", not "int" [assert-type] -directives_assert_type.py:31: error: "assert_type" expects 2 arguments [misc] -directives_assert_type.py:31: error: Too few arguments for "assert_type" [call-arg] -directives_assert_type.py:32: error: Expression is of type "Literal['']", not "int" [assert-type] -directives_assert_type.py:33: error: "assert_type" expects 2 arguments [misc] -directives_assert_type.py:33: error: Too many arguments for "assert_type" [call-arg] +directives_assert_type.py:28: error: Expression is of type "int | str", not "int" [assert-type] +directives_assert_type.py:29: error: Expression is of type "Any", not "int" [assert-type] +directives_assert_type.py:30: error: Expression is of type "Literal[4]", not "int" [assert-type] +directives_assert_type.py:32: error: "assert_type" expects 2 arguments [misc] +directives_assert_type.py:32: error: Too few arguments for "assert_type" [call-arg] +directives_assert_type.py:33: error: Expression is of type "Literal['']", not "int" [assert-type] +directives_assert_type.py:34: error: "assert_type" expects 2 arguments [misc] +directives_assert_type.py:34: error: Too many arguments for "assert_type" [call-arg] """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 41659210a..9799940d7 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.15.0" -test_duration = 1.5 +test_duration = 2.2 diff --git a/conformance/results/pyre/directives_assert_type.toml b/conformance/results/pyre/directives_assert_type.toml index 7c81101d7..15e2fd243 100644 --- a/conformance/results/pyre/directives_assert_type.toml +++ b/conformance/results/pyre/directives_assert_type.toml @@ -2,13 +2,15 @@ conformant = "Pass" notes = """ """ output = """ -directives_assert_type.py:27:4 Assert type [70]: Expected `int` but got `Union[int, str]`. -directives_assert_type.py:28:4 Assert type [70]: Expected `int` but got `typing.Any`. -directives_assert_type.py:29:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[4]`. -directives_assert_type.py:31:4 Missing argument [20]: Call `assert_type` expects argument in position 0. -directives_assert_type.py:32:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal['']`. -directives_assert_type.py:33:4 Too many arguments [19]: Call `assert_type` expects 2 positional arguments, 3 were provided. +directives_assert_type.py:22:4 Assert type [70]: Expected `Union[bool, int, str]` but got `Union[int, str]`. +directives_assert_type.py:28:4 Assert type [70]: Expected `int` but got `Union[int, str]`. +directives_assert_type.py:29:4 Assert type [70]: Expected `int` but got `typing.Any`. +directives_assert_type.py:30:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[4]`. +directives_assert_type.py:32:4 Missing argument [20]: Call `assert_type` expects argument in position 0. +directives_assert_type.py:33:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal['']`. +directives_assert_type.py:34:4 Too many arguments [19]: Call `assert_type` expects 2 positional arguments, 3 were provided. """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 22: Unexpected errors ['directives_assert_type.py:22:4 Assert type [70]: Expected `Union[bool, int, str]` but got `Union[int, str]`.'] """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index c759e78d0..6cd65ec6a 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 10.7 +test_duration = 9.9 diff --git a/conformance/results/pyright/aliases_typealiastype.toml b/conformance/results/pyright/aliases_typealiastype.toml index 732bebde1..8bd8d47da 100644 --- a/conformance/results/pyright/aliases_typealiastype.toml +++ b/conformance/results/pyright/aliases_typealiastype.toml @@ -1,7 +1,4 @@ -conformant = "Partial" -notes = """ -Incorrectly allows undefined self reference. -""" +conformant = "Pass" output = """ aliases_typealiastype.py:32:18 - error: Cannot access attribute "other_attrib" for class "TypeAliasType"   Attribute "other_attrib" is unknown (reportAttributeAccessIssue) @@ -31,8 +28,8 @@ aliases_typealiastype.py:61:42 - error: Expected class but received "Literal[Tru aliases_typealiastype.py:62:42 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) aliases_typealiastype.py:63:42 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) aliases_typealiastype.py:64:42 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +aliases_typealiastype.py:66:47 - error: "BadAlias21" is not defined (reportUndefinedVariable) """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 66: Expected 1 errors """ diff --git a/conformance/results/pyright/callables_protocol.toml b/conformance/results/pyright/callables_protocol.toml index 47af00471..82afbb4c6 100644 --- a/conformance/results/pyright/callables_protocol.toml +++ b/conformance/results/pyright/callables_protocol.toml @@ -32,11 +32,10 @@ callables_protocol.py:70:7 - error: Type "(**b: str) -> None" is not assignable   Type "(**b: str) -> None" is not assignable to type "(*vals: bytes, **kwargs: str) -> None"     Parameter "*vals" has no corresponding parameter (reportAssignmentType) callables_protocol.py:97:16 - error: Type "(x: int) -> None" is not assignable to declared type "Proto4" -  "function" is incompatible with protocol "Proto4" -    "other_attribute" is not present -    "__call__" is not present (reportAssignmentType) +  "FunctionType" is incompatible with protocol "Proto4" +    "other_attribute" is not present (reportAssignmentType) callables_protocol.py:121:18 - error: Type "(*vals: bytes, max_len: int | None = None) -> list[bytes]" is not assignable to declared type "NotProto6" -  "function" is not assignable to "NotProto6" (reportAssignmentType) +  "FunctionType" is not assignable to "NotProto6" (reportAssignmentType) callables_protocol.py:169:7 - error: Type "(x: int) -> Any" is not assignable to declared type "Proto8"   One or more overloads of "__call__" is not assignable     Type "(x: int) -> Any" is not assignable to type "(x: str) -> str" diff --git a/conformance/results/pyright/directives_assert_type.toml b/conformance/results/pyright/directives_assert_type.toml index 9c37b0b04..f746d1761 100644 --- a/conformance/results/pyright/directives_assert_type.toml +++ b/conformance/results/pyright/directives_assert_type.toml @@ -1,12 +1,17 @@ -conformant = "Pass" +conformant = "Fail" +notes = """ +Fails to treat equivalent unions as equivalent. +""" output = """ -directives_assert_type.py:27:17 - error: "assert_type" mismatch: expected "int" but received "int | str" (reportAssertTypeFailure) -directives_assert_type.py:28:17 - error: "assert_type" mismatch: expected "int" but received "Any" (reportAssertTypeFailure) -directives_assert_type.py:29:17 - error: "assert_type" mismatch: expected "int" but received "Literal[4]" (reportAssertTypeFailure) -directives_assert_type.py:31:5 - error: "assert_type" expects two positional arguments (reportCallIssue) -directives_assert_type.py:32:17 - error: "assert_type" mismatch: expected "int" but received "Literal['']" (reportAssertTypeFailure) -directives_assert_type.py:33:5 - error: "assert_type" expects two positional arguments (reportCallIssue) +directives_assert_type.py:22:17 - error: "assert_type" mismatch: expected "int | bool | str" but received "int | str" (reportAssertTypeFailure) +directives_assert_type.py:28:17 - error: "assert_type" mismatch: expected "int" but received "int | str" (reportAssertTypeFailure) +directives_assert_type.py:29:17 - error: "assert_type" mismatch: expected "int" but received "Any" (reportAssertTypeFailure) +directives_assert_type.py:30:17 - error: "assert_type" mismatch: expected "int" but received "Literal[4]" (reportAssertTypeFailure) +directives_assert_type.py:32:5 - error: "assert_type" expects two positional arguments (reportCallIssue) +directives_assert_type.py:33:17 - error: "assert_type" mismatch: expected "int" but received "Literal['']" (reportAssertTypeFailure) +directives_assert_type.py:34:5 - error: "assert_type" expects two positional arguments (reportCallIssue) """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 22: Unexpected errors ['directives_assert_type.py:22:17 - error: "assert_type" mismatch: expected "int | bool | str" but received "int | str" (reportAssertTypeFailure)'] """ diff --git a/conformance/results/pyright/generics_syntax_infer_variance.toml b/conformance/results/pyright/generics_syntax_infer_variance.toml index b23985d59..05f4c0d51 100644 --- a/conformance/results/pyright/generics_syntax_infer_variance.toml +++ b/conformance/results/pyright/generics_syntax_infer_variance.toml @@ -6,6 +6,12 @@ generics_syntax_infer_variance.py:29:35 - error: Type "ShouldBeCovariant1[float]   "ShouldBeCovariant1[float]" is not assignable to "ShouldBeCovariant1[int]"     Type parameter "T@ShouldBeCovariant1" is covariant, but "float" is not a subtype of "int"       "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:36:37 - error: Cannot instantiate abstract class "ShouldBeCovariant2" +  "Collection.__len__" is not implemented +  "Sequence.__getitem__" is not implemented (reportAbstractUsage) +generics_syntax_infer_variance.py:37:35 - error: Cannot instantiate abstract class "ShouldBeCovariant2" +  "Collection.__len__" is not implemented +  "Sequence.__getitem__" is not implemented (reportAbstractUsage) generics_syntax_infer_variance.py:37:35 - error: Type "ShouldBeCovariant2[float]" is not assignable to declared type "ShouldBeCovariant2[int]"   "ShouldBeCovariant2[float]" is not assignable to "ShouldBeCovariant2[int]"     Type parameter "T@ShouldBeCovariant2" is covariant, but "float" is not a subtype of "int" @@ -57,6 +63,7 @@ generics_syntax_infer_variance.py:155:45 - error: Type "ShouldBeContravariant1[i     Type parameter "T@ShouldBeContravariant1" is contravariant, but "int" is not a supertype of "float"       "float" is not assignable to "int" (reportAssignmentType) """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 36: Unexpected errors ['generics_syntax_infer_variance.py:36:37 - error: Cannot instantiate abstract class "ShouldBeCovariant2"'] """ diff --git a/conformance/results/pyright/protocols_definition.toml b/conformance/results/pyright/protocols_definition.toml index c27a3753d..0ed86d0ff 100644 --- a/conformance/results/pyright/protocols_definition.toml +++ b/conformance/results/pyright/protocols_definition.toml @@ -46,7 +46,7 @@ protocols_definition.py:160:22 - error: Type "Concrete3_Bad5" is not assignable protocols_definition.py:218:22 - error: Type "Concrete4_Bad1" is not assignable to declared type "Template4"   "Concrete4_Bad1" is incompatible with protocol "Template4"     "val1" is an incompatible type -      "function" is not assignable to "Sequence[float]" (reportAssignmentType) +      "FunctionType" is not assignable to "Sequence[float]" (reportAssignmentType) protocols_definition.py:219:22 - error: Type "Concrete4_Bad2" is not assignable to declared type "Template4"   "Concrete4_Bad2" is incompatible with protocol "Template4"     "val1" is not present (reportAssignmentType) diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index f105339d2..f6feee504 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ -version = "pyright 1.1.400" -test_duration = 1.1 +version = "pyright 1.1.401" +test_duration = 1.5 diff --git a/conformance/results/results.html b/conformance/results/results.html index b881e2373..411981169 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,13 +159,13 @@

Python Type System Conformance Test Results

-
 
mypy 1.15.0
-
1.5sec
+
2.2sec
pyright 1.1.400
-
1.1sec
+
pyright 1.1.401
+
1.5sec
pyre 0.9.23
-
10.7sec
+
9.9sec
diff --git a/conformance/tests/directives_assert_type.py b/conformance/tests/directives_assert_type.py index b5a337378..8cb9e4c21 100644 --- a/conformance/tests/directives_assert_type.py +++ b/conformance/tests/directives_assert_type.py @@ -19,6 +19,7 @@ def func1( e: Annotated[Literal[4], ""], ): assert_type(a, int | str) # OK + assert_type(a, int | bool | str) # OK (equivalent type) assert_type(b, list[int]) # OK assert_type(c, Any) # OK assert_type(d, "ForwardReference") # OK diff --git a/docs/spec/directives.rst b/docs/spec/directives.rst index 62e1b3c4f..6f1aa9e51 100644 --- a/docs/spec/directives.rst +++ b/docs/spec/directives.rst @@ -9,13 +9,15 @@ Type checker directives ----------------- The function ``typing.assert_type(val, typ)`` allows users to -ask a static type checker to confirm that *val* has an inferred type of *typ*. +ask a static type checker to confirm that the inferred type of *val* +is :term:`equivalent` to *typ*. When a type checker encounters a call to ``assert_type()``, it should emit an error if the value is not of the specified type:: def greet(name: str) -> None: assert_type(name, str) # OK, inferred type of `name` is `str` + assert_type(name, str | Literal["spam"]) # OK, this type is equivalent to `str` assert_type(name, int) # type checker error The second argument must be a valid :term:`type expression`.