@@ -115,31 +115,41 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
115
115
MYPY_TRUE if true under mypy and false at runtime, MYPY_FALSE if
116
116
false under mypy and true at runtime, else TRUTH_VALUE_UNKNOWN.
117
117
"""
118
+ if isinstance (expr , UnaryExpr ) and expr .op == "not" :
119
+ positive = infer_condition_value (expr .expr , options )
120
+ return inverted_truth_mapping [positive ]
121
+
118
122
pyversion = options .python_version
119
123
name = ""
120
- negated = False
121
- alias = expr
122
- if isinstance (alias , UnaryExpr ):
123
- if alias .op == "not" :
124
- expr = alias .expr
125
- negated = True
124
+
126
125
result = TRUTH_VALUE_UNKNOWN
127
126
if isinstance (expr , NameExpr ):
128
127
name = expr .name
129
128
elif isinstance (expr , MemberExpr ):
130
129
name = expr .name
131
130
elif isinstance (expr , OpExpr ) and expr .op in ("and" , "or" ):
131
+ # This is a bit frivolous with MYPY_* vs ALWAYS_* returns: for example, here
132
+ # `MYPY_TRUE or ALWAYS_TRUE` will be `MYPY_TRUE`, while
133
+ # `ALWAYS_TRUE or MYPY_TRUE` will be `ALWAYS_TRUE`. This literally never
134
+ # makes any difference in consuming code, so short-circuiting here is probably
135
+ # good enough as it allows referencing platform-dependent variables in
136
+ # statement parts that will not be executed.
132
137
left = infer_condition_value (expr .left , options )
133
- if (left in (ALWAYS_TRUE , MYPY_TRUE ) and expr .op == "and " ) or (
134
- left in (ALWAYS_FALSE , MYPY_FALSE ) and expr .op == "or "
138
+ if (left in (ALWAYS_TRUE , MYPY_TRUE ) and expr .op == "or " ) or (
139
+ left in (ALWAYS_FALSE , MYPY_FALSE ) and expr .op == "and "
135
140
):
136
- # Either `True and <other>` or `False or <other>`: the result will
137
- # always be the right-hand-side.
138
- return infer_condition_value (expr .right , options )
139
- else :
140
- # The result will always be the left-hand-side (e.g. ALWAYS_* or
141
- # TRUTH_VALUE_UNKNOWN).
141
+ # Either `True or <other>` or `False and <other>`: `<other>` doesn't matter
142
142
return left
143
+ right = infer_condition_value (expr .right , options )
144
+ if (right in (ALWAYS_TRUE , MYPY_TRUE ) and expr .op == "or" ) or (
145
+ right in (ALWAYS_FALSE , MYPY_FALSE ) and expr .op == "and"
146
+ ):
147
+ # Either `<other> or True` or `<other> and False`: `<other>` doesn't matter
148
+ return right
149
+ # Now we have `True and True`, `False or False` or smth indeterminate.
150
+ if TRUTH_VALUE_UNKNOWN in (left , right ) or expr .op not in ("or" , "and" ):
151
+ return TRUTH_VALUE_UNKNOWN
152
+ return left
143
153
else :
144
154
result = consider_sys_version_info (expr , pyversion )
145
155
if result == TRUTH_VALUE_UNKNOWN :
@@ -155,8 +165,6 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
155
165
result = ALWAYS_TRUE
156
166
elif name in options .always_false :
157
167
result = ALWAYS_FALSE
158
- if negated :
159
- result = inverted_truth_mapping [result ]
160
168
return result
161
169
162
170
0 commit comments