diff --git a/conformance/results/mypy/directives_assert_type.toml b/conformance/results/mypy/directives_assert_type.toml index a8feed5e..61e4a8a2 100644 --- a/conformance/results/mypy/directives_assert_type.toml +++ b/conformance/results/mypy/directives_assert_type.toml @@ -1,13 +1,14 @@ 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 "Any" [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/pyrefly/directives_assert_type.toml b/conformance/results/pyrefly/directives_assert_type.toml index 35e07db7..bafeb740 100644 --- a/conformance/results/pyrefly/directives_assert_type.toml +++ b/conformance/results/pyrefly/directives_assert_type.toml @@ -4,9 +4,10 @@ errors_diff = """ """ output = """ ERROR directives_assert_type.py:27:16-24: assert_type(int | str, int) failed [assert-type] -ERROR directives_assert_type.py:28:16-24: assert_type(Any, int) failed [assert-type] -ERROR directives_assert_type.py:29:16-24: assert_type(Literal[4], int) failed [assert-type] -ERROR directives_assert_type.py:31:16-18: assert_type needs 2 positional arguments, got 0 [bad-argument-count] -ERROR directives_assert_type.py:32:16-25: assert_type(Literal[''], int) failed [assert-type] -ERROR directives_assert_type.py:33:16-33: assert_type needs 2 positional arguments, got 3 [bad-argument-count] +ERROR directives_assert_type.py:28:16-24: assert_type(int | str, Any) failed [assert-type] +ERROR directives_assert_type.py:29:16-24: assert_type(Any, int) failed [assert-type] +ERROR directives_assert_type.py:30:16-24: assert_type(Literal[4], int) failed [assert-type] +ERROR directives_assert_type.py:32:16-18: assert_type needs 2 positional arguments, got 0 [bad-argument-count] +ERROR directives_assert_type.py:33:16-25: assert_type(Literal[''], int) failed [assert-type] +ERROR directives_assert_type.py:34:16-33: assert_type needs 2 positional arguments, got 3 [bad-argument-count] """ diff --git a/conformance/results/pyright/directives_assert_type.toml b/conformance/results/pyright/directives_assert_type.toml index 9c37b0b0..cb868983 100644 --- a/conformance/results/pyright/directives_assert_type.toml +++ b/conformance/results/pyright/directives_assert_type.toml @@ -1,11 +1,13 @@ conformant = "Pass" 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:28:17 - error: "assert_type" mismatch: expected "Any" 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) +directives_assert_type.py:41:17 - error: "assert_type" mismatch: expected "str | Literal['spam']" but received "str" (reportAssertTypeFailure) """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/zuban/directives_assert_type.toml b/conformance/results/zuban/directives_assert_type.toml index 15ed60cb..154dfded 100644 --- a/conformance/results/zuban/directives_assert_type.toml +++ b/conformance/results/zuban/directives_assert_type.toml @@ -3,9 +3,11 @@ errors_diff = """ """ output = """ directives_assert_type.py:27: error: Expression is of type "int | str", not "int" [misc] -directives_assert_type.py:28: error: Expression is of type "Any", not "int" [misc] -directives_assert_type.py:29: error: Expression is of type "Literal[4]", not "int" [misc] -directives_assert_type.py:31: error: "assert_type" expects 2 arguments [call-arg] -directives_assert_type.py:32: error: Expression is of type "Literal['']", not "int" [misc] -directives_assert_type.py:33: error: "assert_type" expects 2 arguments [call-arg] +directives_assert_type.py:28: error: Expression is of type "int | str", not "Any" [misc] +directives_assert_type.py:29: error: Expression is of type "Any", not "int" [misc] +directives_assert_type.py:30: error: Expression is of type "Literal[4]", not "int" [misc] +directives_assert_type.py:32: error: "assert_type" expects 2 arguments [call-arg] +directives_assert_type.py:33: error: Expression is of type "Literal['']", not "int" [misc] +directives_assert_type.py:34: error: "assert_type" expects 2 arguments [call-arg] +directives_assert_type.py:41: error: Expression is of type "str", not "str | Literal['spam']" [misc] """ diff --git a/conformance/tests/directives_assert_type.py b/conformance/tests/directives_assert_type.py index b5a33737..900529b1 100644 --- a/conformance/tests/directives_assert_type.py +++ b/conformance/tests/directives_assert_type.py @@ -25,6 +25,7 @@ def func1( assert_type(e, Literal[4]) # OK assert_type(a, int) # E: Type mismatch + assert_type(a, Any) # E: Type mismatch assert_type(c, int) # E: Type mismatch assert_type(e, int) # E: Type mismatch @@ -33,5 +34,12 @@ def func1( assert_type(a, int | str, a) # E: too many arguments +# > If the two types are :term:`equivalent` but syntactically different, +# > the type checker may reject the ``assert_type()`` call:: + +def func2(name: str): + assert_type(name, str | Literal["spam"]) # E?: Equivalent but not identical + + class ForwardReference: pass diff --git a/docs/spec/directives.rst b/docs/spec/directives.rst index 3693af49..6ed7016a 100644 --- a/docs/spec/directives.rst +++ b/docs/spec/directives.rst @@ -9,7 +9,8 @@ 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:: @@ -18,6 +19,17 @@ should emit an error if the value is not of the specified type:: assert_type(name, str) # OK, inferred type of `name` is `str` assert_type(name, int) # type checker error +If the two types are :term:`equivalent` but syntactically different, +the type checker may reject the ``assert_type()`` call:: + + from typing import assert_type, Literal + + def greet(name: str) -> None: + assert_type(name, str | Literal["spam"]) # type checker may error + +Type checkers should aim to minimize cases where they reject +``assert_type()`` calls that use equivalent types. + The second argument must be a valid :term:`type expression`. .. _`reveal-type`: