You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor visit_conditional_expr to fix ternary behavior (#19563)
- Fixes#18817
- added unit test `testTernaryOperatorWithDefault`
- Fixes#19561
- added unit tests `testLambdaTernary{, IndirectAttribute,
DoubleIndirectAttribute}`
- Fixes#19534
- added unit tests `test{List, Set, Dict}ComprehensionTernary`
- Fixes#19998
Option ① from
#19561 (comment)
which does not allow `MemberExpr` elements to the nested binder, hence
ternaries like `f(x.attr) if x.attr else g(x.attr)` will not consider
the narrowed type of `x.attr`
Option ②: #19562
The current implementation of `visit_conditional_expr` seemed to some
rather complicated things, I found that if there is no context, we can
simply use the union of the types produced when considering the branches
context-free as an artificial context that leads to the desired behavior
in the unification test cases.
Copy file name to clipboardExpand all lines: test-data/unit/check-literal.test
+137Lines changed: 137 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -2949,6 +2949,143 @@ reveal_type(C().collection) # N: Revealed type is "builtins.list[Literal['word'
2949
2949
reveal_type(C().word) # N: Revealed type is "Literal['word']"
2950
2950
[builtins fixtures/tuple.pyi]
2951
2951
2952
+
[case testStringLiteralTernary]
2953
+
# https://github.com/python/mypy/issues/19534
2954
+
def test(b: bool) -> None:
2955
+
l = "foo" if b else "bar"
2956
+
reveal_type(l) # N: Revealed type is "builtins.str"
2957
+
[builtins fixtures/tuple.pyi]
2958
+
2959
+
[case testintLiteralTernary]
2960
+
# https://github.com/python/mypy/issues/19534
2961
+
def test(b: bool) -> None:
2962
+
l = 0 if b else 1
2963
+
reveal_type(l) # N: Revealed type is "builtins.int"
2964
+
[builtins fixtures/tuple.pyi]
2965
+
2966
+
[case testStringIntUnionTernary]
2967
+
# https://github.com/python/mypy/issues/19534
2968
+
def test(b: bool) -> None:
2969
+
l = 1 if b else "a"
2970
+
reveal_type(l) # N: Revealed type is "Union[builtins.int, builtins.str]"
2971
+
[builtins fixtures/tuple.pyi]
2972
+
2973
+
[case testListComprehensionTernary]
2974
+
# https://github.com/python/mypy/issues/19534
2975
+
def test(b: bool) -> None:
2976
+
l = [1] if b else ["a"]
2977
+
reveal_type(l) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]"
2978
+
[builtins fixtures/list.pyi]
2979
+
2980
+
[case testSetComprehensionTernary]
2981
+
# https://github.com/python/mypy/issues/19534
2982
+
def test(b: bool) -> None:
2983
+
s = {1} if b else {"a"}
2984
+
reveal_type(s) # N: Revealed type is "Union[builtins.set[builtins.int], builtins.set[builtins.str]]"
2985
+
[builtins fixtures/set.pyi]
2986
+
2987
+
[case testDictComprehensionTernary]
2988
+
# https://github.com/python/mypy/issues/19534
2989
+
def test(b: bool) -> None:
2990
+
d = {1:1} if "" else {"a": "a"}
2991
+
reveal_type(d) # N: Revealed type is "Union[builtins.dict[builtins.int, builtins.int], builtins.dict[builtins.str, builtins.str]]"
2992
+
[builtins fixtures/dict.pyi]
2993
+
2994
+
[case testLambdaTernary]
2995
+
from typing import TypeVar, Union, Callable, reveal_type
2996
+
2997
+
NOOP = lambda: None
2998
+
class A: pass
2999
+
class B:
3000
+
attr: Union[A, None]
3001
+
3002
+
def test_static(x: Union[A, None]) -> None:
3003
+
def foo(t: A) -> None: ...
3004
+
3005
+
l1: Callable[[], object] = (lambda: foo(x)) if x is not None else NOOP
3006
+
r1: Callable[[], object] = NOOP if x is None else (lambda: foo(x))
3007
+
l2 = (lambda: foo(x)) if x is not None else NOOP
3008
+
r2 = NOOP if x is None else (lambda: foo(x))
3009
+
reveal_type(l2) # N: Revealed type is "def ()"
3010
+
reveal_type(r2) # N: Revealed type is "def ()"
3011
+
3012
+
def test_generic(x: Union[A, None]) -> None:
3013
+
T = TypeVar("T")
3014
+
def bar(t: T) -> T: return t
3015
+
3016
+
l1: Callable[[], None] = (lambda: bar(x)) if x is None else NOOP
3017
+
r1: Callable[[], None] = NOOP if x is not None else (lambda: bar(x))
3018
+
l2 = (lambda: bar(x)) if x is None else NOOP
3019
+
r2 = NOOP if x is not None else (lambda: bar(x))
3020
+
reveal_type(l2) # N: Revealed type is "def ()"
3021
+
reveal_type(r2) # N: Revealed type is "def ()"
3022
+
3023
+
3024
+
[case testLambdaTernaryIndirectAttribute]
3025
+
# fails due to binder issue inside `check_func_def`
3026
+
# https://github.com/python/mypy/issues/19561
3027
+
from typing import TypeVar, Union, Callable, reveal_type
3028
+
3029
+
NOOP = lambda: None
3030
+
class A: pass
3031
+
class B:
3032
+
attr: Union[A, None]
3033
+
3034
+
def test_static_with_attr(x: B) -> None:
3035
+
def foo(t: A) -> None: ...
3036
+
3037
+
l1: Callable[[], None] = (lambda: foo(x.attr)) if x.attr is not None else NOOP # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3038
+
r1: Callable[[], None] = NOOP if x.attr is None else (lambda: foo(x.attr)) # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3039
+
l2 = (lambda: foo(x.attr)) if x.attr is not None else NOOP # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3040
+
r2 = NOOP if x.attr is None else (lambda: foo(x.attr)) # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3041
+
reveal_type(l2) # N: Revealed type is "def ()"
3042
+
reveal_type(r2) # N: Revealed type is "def ()"
3043
+
3044
+
def test_generic_with_attr(x: B) -> None:
3045
+
T = TypeVar("T")
3046
+
def bar(t: T) -> T: return t
3047
+
3048
+
l1: Callable[[], None] = (lambda: bar(x.attr)) if x.attr is None else NOOP # E: Incompatible types in assignment (expression has type "Callable[[], Optional[A]]", variable has type "Callable[[], None]")
3049
+
r1: Callable[[], None] = NOOP if x.attr is not None else (lambda: bar(x.attr)) # E: Incompatible types in assignment (expression has type "Callable[[], Optional[A]]", variable has type "Callable[[], None]")
3050
+
l2 = (lambda: bar(x.attr)) if x.attr is None else NOOP
3051
+
r2 = NOOP if x.attr is not None else (lambda: bar(x.attr))
3052
+
reveal_type(l2) # N: Revealed type is "def () -> Union[__main__.A, None]"
3053
+
reveal_type(r2) # N: Revealed type is "def () -> Union[__main__.A, None]"
3054
+
3055
+
[case testLambdaTernaryDoubleIndirectAttribute]
3056
+
# fails due to binder issue inside `check_func_def`
3057
+
# https://github.com/python/mypy/issues/19561
3058
+
from typing import TypeVar, Union, Callable, reveal_type
3059
+
3060
+
NOOP = lambda: None
3061
+
class A: pass
3062
+
class B:
3063
+
attr: Union[A, None]
3064
+
class C:
3065
+
attr: B
3066
+
3067
+
def test_static_with_attr(x: C) -> None:
3068
+
def foo(t: A) -> None: ...
3069
+
3070
+
l1: Callable[[], None] = (lambda: foo(x.attr.attr)) if x.attr.attr is not None else NOOP # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3071
+
r1: Callable[[], None] = NOOP if x.attr.attr is None else (lambda: foo(x.attr.attr)) # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3072
+
l2 = (lambda: foo(x.attr.attr)) if x.attr.attr is not None else NOOP # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3073
+
r2 = NOOP if x.attr.attr is None else (lambda: foo(x.attr.attr)) # E: Argument 1 to "foo" has incompatible type "Optional[A]"; expected "A"
3074
+
reveal_type(l2) # N: Revealed type is "def ()"
3075
+
reveal_type(r2) # N: Revealed type is "def ()"
3076
+
3077
+
def test_generic_with_attr(x: C) -> None:
3078
+
T = TypeVar("T")
3079
+
def bar(t: T) -> T: return t
3080
+
3081
+
l1: Callable[[], None] = (lambda: bar(x.attr.attr)) if x.attr.attr is None else NOOP # E: Incompatible types in assignment (expression has type "Callable[[], Optional[A]]", variable has type "Callable[[], None]")
3082
+
r1: Callable[[], None] = NOOP if x.attr.attr is not None else (lambda: bar(x.attr.attr)) # E: Incompatible types in assignment (expression has type "Callable[[], Optional[A]]", variable has type "Callable[[], None]")
3083
+
l2 = (lambda: bar(x.attr.attr)) if x.attr.attr is None else NOOP
3084
+
r2 = NOOP if x.attr.attr is not None else (lambda: bar(x.attr.attr))
3085
+
reveal_type(l2) # N: Revealed type is "def () -> Union[__main__.A, None]"
3086
+
reveal_type(r2) # N: Revealed type is "def () -> Union[__main__.A, None]"
0 commit comments