diff --git a/conformance/results/mypy/literals_interactions.toml b/conformance/results/mypy/literals_interactions.toml index c7b83588..a9f03f04 100644 --- a/conformance/results/mypy/literals_interactions.toml +++ b/conformance/results/mypy/literals_interactions.toml @@ -1,17 +1,20 @@ -conformant = "Partial" notes = """ -Does not narrow type of `x` with `x in Literal` type guard pattern. +Does not narrow `str` or `LiteralString` types to `Literal` string types via equality or containment checks. """ output = """ +literals_interactions.py:14: error: Tuple index out of range [misc] literals_interactions.py:15: error: Tuple index out of range [misc] literals_interactions.py:16: error: Tuple index out of range [misc] literals_interactions.py:17: error: Tuple index out of range [misc] -literals_interactions.py:18: error: Tuple index out of range [misc] -literals_interactions.py:106: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] -literals_interactions.py:109: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal['PENDING']" [arg-type] +literals_interactions.py:111: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:113: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:116: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:119: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal['PENDING']" [arg-type] +literals_interactions.py:128: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:130: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:133: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:136: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal['PENDING']" [arg-type] """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 106: Unexpected errors ['literals_interactions.py:106: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal[\\'MALFORMED\\', \\'ABORTED\\']" [arg-type]'] -Line 109: Unexpected errors ['literals_interactions.py:109: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal[\\'PENDING\\']" [arg-type]'] """ diff --git a/conformance/results/pyrefly/literals_interactions.toml b/conformance/results/pyrefly/literals_interactions.toml index 881854c8..451fa2d4 100644 --- a/conformance/results/pyrefly/literals_interactions.toml +++ b/conformance/results/pyrefly/literals_interactions.toml @@ -3,8 +3,8 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ -ERROR literals_interactions.py:15:7-8: Index 5 out of range for tuple with 3 elements [bad-index] -ERROR literals_interactions.py:16:7-8: Index -5 out of range for tuple with 3 elements [bad-index] -ERROR literals_interactions.py:17:7-8: Index 4 out of range for tuple with 3 elements [bad-index] -ERROR literals_interactions.py:18:7-9: Index -4 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:14:7-8: Index 5 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:15:7-8: Index -5 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:16:7-8: Index 4 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:17:7-9: Index -4 out of range for tuple with 3 elements [bad-index] """ diff --git a/conformance/results/pyright/literals_interactions.toml b/conformance/results/pyright/literals_interactions.toml index cd51f439..1ecbc1f0 100644 --- a/conformance/results/pyright/literals_interactions.toml +++ b/conformance/results/pyright/literals_interactions.toml @@ -1,9 +1,9 @@ conformant = "Pass" output = """ -literals_interactions.py:15:5 - error: Index 5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) -literals_interactions.py:16:5 - error: Index -5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) -literals_interactions.py:17:5 - error: Index 4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) -literals_interactions.py:18:5 - error: Index -4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:14:5 - error: Index 5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:15:5 - error: Index -5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:16:5 - error: Index 4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:17:5 - error: Index -4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 249f6b70..adf3ffc2 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -569,11 +569,11 @@

Python Type System Conformance Test Results

Literals      literals_interactions -
Partial

Does not narrow type of `x` with `x in Literal` type guard pattern.

+
Pass*

Does not narrow `str` or `LiteralString` types to `Literal` string types via equality or containment checks.

Pass Pass Pass -
Partial

Deliberately does not allow `str` to be narrowed to literal string types through equality or containment checks due to the possibility of `str` subclasses that could have unexpected equality semantics.

+
Pass*

Deliberately does not allow `str` to be narrowed to literal string types through equality or containment checks due to the possibility of `str` subclasses that could have unexpected equality semantics.

Incorrectly fails to narrow the type `LiteralString & ~Literal["MALFORMED"]` to `Literal["ABORTED"]` after an `== "ABORTED"` check.

     literals_literalstring
Unsupported

Support for `LiteralString` is not implemented.

diff --git a/conformance/results/ty/literals_interactions.toml b/conformance/results/ty/literals_interactions.toml index 354e5a8f..3310f7c7 100644 --- a/conformance/results/ty/literals_interactions.toml +++ b/conformance/results/ty/literals_interactions.toml @@ -1,17 +1,18 @@ -conformance_automated = "Fail" -conformant = "Partial" +conformance_automated = "Pass" notes = """ Deliberately does not allow `str` to be narrowed to literal string types through equality or containment checks due to the possibility of `str` subclasses that could have unexpected equality semantics. +Incorrectly fails to narrow the type `LiteralString & ~Literal["MALFORMED"]` to `Literal["ABORTED"]` after an `== "ABORTED"` check. """ errors_diff = """ -Line 106: Unexpected errors ['literals_interactions.py:106:35: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str`'] -Line 109: Unexpected errors ['literals_interactions.py:109:32: error[invalid-argument-type] Argument to function `expects_pending_status` is incorrect: Expected `Literal["PENDING"]`, found `str`'] """ output = """ -literals_interactions.py:15:5: error[index-out-of-bounds] Index 5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 -literals_interactions.py:16:5: error[index-out-of-bounds] Index -5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 -literals_interactions.py:17:5: error[index-out-of-bounds] Index 4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 -literals_interactions.py:18:5: error[index-out-of-bounds] Index -4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 -literals_interactions.py:106:35: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str` -literals_interactions.py:109:32: error[invalid-argument-type] Argument to function `expects_pending_status` is incorrect: Expected `Literal["PENDING"]`, found `str` +literals_interactions.py:14:5: error[index-out-of-bounds] Index 5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:15:5: error[index-out-of-bounds] Index -5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:16:5: error[index-out-of-bounds] Index 4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:17:5: error[index-out-of-bounds] Index -4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:113:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `LiteralString & ~Literal["MALFORMED"]` +literals_interactions.py:128:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str` +literals_interactions.py:130:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str & ~Literal["MALFORMED"]` +literals_interactions.py:133:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str` +literals_interactions.py:136:32: error[invalid-argument-type] Argument to function `expects_pending_status` is incorrect: Expected `Literal["PENDING"]`, found `str` """ diff --git a/conformance/results/zuban/literals_interactions.toml b/conformance/results/zuban/literals_interactions.toml index 242baa2f..16633617 100644 --- a/conformance/results/zuban/literals_interactions.toml +++ b/conformance/results/zuban/literals_interactions.toml @@ -2,8 +2,8 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ +literals_interactions.py:14: error: Tuple index out of range [misc] literals_interactions.py:15: error: Tuple index out of range [misc] literals_interactions.py:16: error: Tuple index out of range [misc] literals_interactions.py:17: error: Tuple index out of range [misc] -literals_interactions.py:18: error: Tuple index out of range [misc] """ diff --git a/conformance/tests/literals_interactions.py b/conformance/tests/literals_interactions.py index b7277325..47412676 100644 --- a/conformance/tests/literals_interactions.py +++ b/conformance/tests/literals_interactions.py @@ -3,9 +3,8 @@ """ # Specification: https://typing.readthedocs.io/en/latest/spec/literal.html#interactions-with-other-types-and-features - from enum import Enum -from typing import IO, Any, Final, Generic, Literal, TypeVar, assert_type, overload +from typing import IO, Any, Final, Generic, Literal, TypeVar, LiteralString, assert_type, overload def func1(v: tuple[int, str, list[bool]], a: Literal[0], b: Literal[5], c: Literal[-5]): @@ -93,6 +92,12 @@ def parse_status1(s: str | Status) -> None: assert_type(s, str) +# > Type checkers may optionally perform additional analysis for both +# > enum and non-enum Literal types beyond what is described in the section above. +# +# > For example, it may be useful to perform narrowing based on things +# > like containment or equality checks: + def expects_bad_status(status: Literal["MALFORMED", "ABORTED"]): ... @@ -101,12 +106,34 @@ def expects_pending_status(status: Literal["PENDING"]): ... -def parse_status2(status: str) -> None: +def parse_status2(status: LiteralString) -> None: + if status == "MALFORMED": + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + elif status == "ABORTED": + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + + if status in ("MALFORMED", "ABORTED"): + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + + if status == "PENDING": + expects_pending_status(status) # E? narrowing the type here is sound, but optional per the spec + + +# Narrowing `str` to `Literal` strings is unsound given the possiblity of +# user-defined `str` subclasses that could have custom equality semantics, +# but is explicitly listed by the spec as optional analysis that type checkers +# may perform. +def parse_status3(status: str) -> None: + if status == "MALFORMED": + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec + elif status == "ABORTED": + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec + if status in ("MALFORMED", "ABORTED"): - return expects_bad_status(status) + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec if status == "PENDING": - expects_pending_status(status) + expects_pending_status(status) # E? narrowing the type here is unsound, but allowed per the spec final_val1: Final = 3