← Back to index

literals_interactions.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 4
FP: 0
FN: 0
Optional: 4 / 8
1"""
2Tests interactions between Literal types and other typing features.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/literal.html#interactions-with-other-types-and-features
6from enum import Enum
7from typing import IO, Any, Final, Generic, Literal, TypeVar, LiteralString, assert_type, overload
8
9
10def func1(v: tuple[int, str, list[bool]], a: Literal[0], b: Literal[5], c: Literal[-5]):
11 assert_type(v[a], int)
12 assert_type(v[2], list[bool])
14 v[b] # E: index out of range
[index-out-of-bounds] Index 5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3
15 v[c] # E: index out of range
[index-out-of-bounds] Index -5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3
16 v[4] # E: index out of range
[index-out-of-bounds] Index 4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3
17 v[-4] # E: index out of range
[index-out-of-bounds] Index -4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3
20_PathType = str | bytes | int
23@overload
24def open(
25 path: _PathType,
26 mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"],
27) -> IO[str]:
28 ...
31@overload
32def open(
33 path: _PathType,
34 mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"],
35) -> IO[bytes]:
36 ...
39@overload
40def open(path: _PathType, mode: str) -> IO[Any]:
41 ...
44def open(path: _PathType, mode: Any) -> Any:
45 raise NotImplementedError
48assert_type(open("path", "r"), IO[str])
49assert_type(open("path", "wb"), IO[bytes])
50assert_type(open("path", "other"), IO[Any])
53A = TypeVar("A", bound=int)
54B = TypeVar("B", bound=int)
55C = TypeVar("C", bound=int)
58class Matrix(Generic[A, B]):
59 def __add__(self, other: "Matrix[A, B]") -> "Matrix[A, B]":
60 raise NotImplementedError
62 def __matmul__(self, other: "Matrix[B, C]") -> "Matrix[A, C]":
63 raise NotImplementedError
65 def transpose(self) -> "Matrix[B, A]":
66 raise NotImplementedError
69def func2(a: Matrix[Literal[2], Literal[3]], b: Matrix[Literal[3], Literal[7]]):
70 c = a @ b
71 assert_type(c, Matrix[Literal[2], Literal[7]])
74T = TypeVar("T", Literal["a"], Literal["b"], Literal["c"])
75S = TypeVar("S", bound=Literal["foo"])
78class Status(Enum):
79 SUCCESS = 0
80 INVALID_DATA = 1
81 FATAL_ERROR = 2
84def parse_status1(s: str | Status) -> None:
85 if s is Status.SUCCESS:
86 assert_type(s, Literal[Status.SUCCESS])
87 elif s is Status.INVALID_DATA:
88 assert_type(s, Literal[Status.INVALID_DATA])
89 elif s is Status.FATAL_ERROR:
90 assert_type(s, Literal[Status.FATAL_ERROR])
91 else:
92 assert_type(s, str)
95# > Type checkers may optionally perform additional analysis for both
96# > enum and non-enum Literal types beyond what is described in the section above.
97#
98# > For example, it may be useful to perform narrowing based on things
99# > like containment or equality checks:
101def expects_bad_status(status: Literal["MALFORMED", "ABORTED"]):
102 ...
105def expects_pending_status(status: Literal["PENDING"]):
106 ...
109def parse_status2(status: LiteralString) -> None:
110 if status == "MALFORMED":
111 expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec
112 elif status == "ABORTED":
113 expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec
115 if status in ("MALFORMED", "ABORTED"):
116 expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec
118 if status == "PENDING":
119 expects_pending_status(status) # E? narrowing the type here is sound, but optional per the spec
122# Narrowing `str` to `Literal` strings is unsound given the possiblity of
123# user-defined `str` subclasses that could have custom equality semantics,
124# but is explicitly listed by the spec as optional analysis that type checkers
125# may perform.
126def parse_status3(status: str) -> None:
127 if status == "MALFORMED":
128 expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec
[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str`
129 elif status == "ABORTED":
130 expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec
[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str & ~Literal["MALFORMED"]`
132 if status in ("MALFORMED", "ABORTED"):
133 expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec
[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str`
135 if status == "PENDING":
136 expects_pending_status(status) # E? narrowing the type here is unsound, but allowed per the spec
[invalid-argument-type] Argument to function `expects_pending_status` is incorrect: Expected `Literal["PENDING"]`, found `str`
139final_val1: Final = 3
140assert_type(final_val1, Literal[3])
142final_val2: Final = True
143assert_type(final_val2, Literal[True])