Skip to content

Commit 60472bb

Browse files
committed
Merge branch 'master' into fix-spurious-newlines-in-blog-post-generator
2 parents 0a065e4 + 5aa3457 commit 60472bb

18 files changed

+139
-153
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
...
66

7-
## Mypy 1.15 (Unreleased)
7+
## Mypy 1.15
88

99
We’ve just uploaded mypy 1.15 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
1010
Mypy is a static type checker for Python. This release includes new features, performance

mypy/binder.py

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from typing_extensions import TypeAlias as _TypeAlias
88

99
from mypy.erasetype import remove_instance_last_known_values
10-
from mypy.join import join_simple
1110
from mypy.literals import Key, literal, literal_hash, subkeys
1211
from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var
1312
from mypy.subtypes import is_same_type, is_subtype
13+
from mypy.typeops import make_simplified_union
1414
from mypy.types import (
1515
AnyType,
1616
Instance,
@@ -21,6 +21,7 @@
2121
Type,
2222
TypeOfAny,
2323
TypeType,
24+
TypeVarType,
2425
UnionType,
2526
UnpackType,
2627
find_unpack_in_list,
@@ -237,9 +238,21 @@ def update_from_options(self, frames: list[Frame]) -> bool:
237238
):
238239
type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type)
239240
else:
240-
for other in resulting_values[1:]:
241-
assert other is not None
242-
type = join_simple(self.declarations[key], type, other.type)
241+
possible_types = []
242+
for t in resulting_values:
243+
assert t is not None
244+
possible_types.append(t.type)
245+
if len(possible_types) == 1:
246+
# This is to avoid calling get_proper_type() unless needed, as this may
247+
# interfere with our (hacky) TypeGuard support.
248+
type = possible_types[0]
249+
else:
250+
type = make_simplified_union(possible_types)
251+
# Legacy guard for corner case when the original type is TypeVarType.
252+
if isinstance(declaration_type, TypeVarType) and not is_subtype(
253+
type, declaration_type
254+
):
255+
type = declaration_type
243256
# Try simplifying resulting type for unions involving variadic tuples.
244257
# Technically, everything is still valid without this step, but if we do
245258
# not do this, this may create long unions after exiting an if check like:
@@ -258,7 +271,7 @@ def update_from_options(self, frames: list[Frame]) -> bool:
258271
)
259272
if simplified == self.declarations[key]:
260273
type = simplified
261-
if current_value is None or not is_same_type(type, current_value[0]):
274+
if current_value is None or not is_same_type(type, current_value.type):
262275
self._put(key, type, from_assignment=True)
263276
changed = True
264277

@@ -300,9 +313,7 @@ def accumulate_type_assignments(self) -> Iterator[Assigns]:
300313
yield self.type_assignments
301314
self.type_assignments = old_assignments
302315

303-
def assign_type(
304-
self, expr: Expression, type: Type, declared_type: Type | None, restrict_any: bool = False
305-
) -> None:
316+
def assign_type(self, expr: Expression, type: Type, declared_type: Type | None) -> None:
306317
# We should erase last known value in binder, because if we are using it,
307318
# it means that the target is not final, and therefore can't hold a literal.
308319
type = remove_instance_last_known_values(type)
@@ -333,41 +344,39 @@ def assign_type(
333344

334345
p_declared = get_proper_type(declared_type)
335346
p_type = get_proper_type(type)
336-
enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type))
337-
if isinstance(enclosing_type, AnyType) and not restrict_any:
338-
# If x is Any and y is int, after x = y we do not infer that x is int.
339-
# This could be changed.
340-
# Instead, since we narrowed type from Any in a recent frame (probably an
341-
# isinstance check), but now it is reassigned, we broaden back
342-
# to Any (which is the most recent enclosing type)
343-
self.put(expr, enclosing_type)
344-
# As a special case, when assigning Any to a variable with a
345-
# declared Optional type that has been narrowed to None,
346-
# replace all the Nones in the declared Union type with Any.
347-
# This overrides the normal behavior of ignoring Any assignments to variables
348-
# in order to prevent false positives.
349-
# (See discussion in #3526)
350-
elif (
351-
isinstance(p_type, AnyType)
352-
and isinstance(p_declared, UnionType)
353-
and any(isinstance(get_proper_type(item), NoneType) for item in p_declared.items)
354-
and isinstance(
355-
get_proper_type(self.most_recent_enclosing_type(expr, NoneType())), NoneType
356-
)
357-
):
358-
# Replace any Nones in the union type with Any
359-
new_items = [
360-
type if isinstance(get_proper_type(item), NoneType) else item
361-
for item in p_declared.items
362-
]
363-
self.put(expr, UnionType(new_items))
364-
elif isinstance(p_type, AnyType) and not (
365-
isinstance(p_declared, UnionType)
366-
and any(isinstance(get_proper_type(item), AnyType) for item in p_declared.items)
367-
):
368-
# Assigning an Any value doesn't affect the type to avoid false negatives, unless
369-
# there is an Any item in a declared union type.
370-
self.put(expr, declared_type)
347+
if isinstance(p_type, AnyType):
348+
# Any type requires some special casing, for both historical reasons,
349+
# and to optimise user experience without sacrificing correctness too much.
350+
if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred:
351+
# First case: a local/global variable without explicit annotation,
352+
# in this case we just assign Any (essentially following the SSA logic).
353+
self.put(expr, type)
354+
elif isinstance(p_declared, UnionType) and any(
355+
isinstance(get_proper_type(item), NoneType) for item in p_declared.items
356+
):
357+
# Second case: explicit optional type, in this case we optimize for a common
358+
# pattern when an untyped value used as a fallback replacing None.
359+
new_items = [
360+
type if isinstance(get_proper_type(item), NoneType) else item
361+
for item in p_declared.items
362+
]
363+
self.put(expr, UnionType(new_items))
364+
elif isinstance(p_declared, UnionType) and any(
365+
isinstance(get_proper_type(item), AnyType) for item in p_declared.items
366+
):
367+
# Third case: a union already containing Any (most likely from an un-imported
368+
# name), in this case we allow assigning Any as well.
369+
self.put(expr, type)
370+
else:
371+
# In all other cases we don't narrow to Any to minimize false negatives.
372+
self.put(expr, declared_type)
373+
elif isinstance(p_declared, AnyType):
374+
# Mirroring the first case above, we don't narrow to a precise type if the variable
375+
# has an explicit `Any` type annotation.
376+
if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred:
377+
self.put(expr, type)
378+
else:
379+
self.put(expr, declared_type)
371380
else:
372381
self.put(expr, type)
373382

@@ -389,19 +398,6 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None:
389398
for dep in self.dependencies.get(key, set()):
390399
self._cleanse_key(dep)
391400

392-
def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type | None:
393-
type = get_proper_type(type)
394-
if isinstance(type, AnyType):
395-
return get_declaration(expr)
396-
key = literal_hash(expr)
397-
assert key is not None
398-
enclosers = [get_declaration(expr)] + [
399-
f.types[key].type
400-
for f in self.frames
401-
if key in f.types and is_subtype(type, f.types[key][0])
402-
]
403-
return enclosers[-1]
404-
405401
def allow_jump(self, index: int) -> None:
406402
# self.frames and self.options_on_return have different lengths
407403
# so make sure the index is positive

mypy/checker.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3284,7 +3284,7 @@ def check_assignment(
32843284
if rvalue_type and infer_lvalue_type and not isinstance(lvalue_type, PartialType):
32853285
# Don't use type binder for definitions of special forms, like named tuples.
32863286
if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form):
3287-
self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False)
3287+
self.binder.assign_type(lvalue, rvalue_type, lvalue_type)
32883288
if (
32893289
isinstance(lvalue, NameExpr)
32903290
and isinstance(lvalue.node, Var)
@@ -4023,7 +4023,7 @@ def check_multi_assignment_from_union(
40234023
if isinstance(expr, StarExpr):
40244024
expr = expr.expr
40254025

4026-
# TODO: See todo in binder.py, ConditionalTypeBinder.assign_type
4026+
# TODO: See comment in binder.py, ConditionalTypeBinder.assign_type
40274027
# It's unclear why the 'declared_type' param is sometimes 'None'
40284028
clean_items: list[tuple[Type, Type]] = []
40294029
for type, declared_type in items:
@@ -4035,7 +4035,6 @@ def check_multi_assignment_from_union(
40354035
expr,
40364036
make_simplified_union(list(types)),
40374037
make_simplified_union(list(declared_types)),
4038-
False,
40394038
)
40404039
for union, lv in zip(union_types, self.flatten_lvalues(lvalues)):
40414040
# Properly store the inferred types.
@@ -5233,7 +5232,7 @@ def visit_del_stmt(self, s: DelStmt) -> None:
52335232
for elt in flatten(s.expr):
52345233
if isinstance(elt, NameExpr):
52355234
self.binder.assign_type(
5236-
elt, DeletedType(source=elt.name), get_declaration(elt), False
5235+
elt, DeletedType(source=elt.name), get_declaration(elt)
52375236
)
52385237

52395238
def visit_decorator(self, e: Decorator) -> None:

mypy/fastparse.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,9 @@ def make_argument(
11061106
if argument_elide_name(arg.arg):
11071107
pos_only = True
11081108

1109-
argument = Argument(Var(arg.arg, arg_type), arg_type, self.visit(default), kind, pos_only)
1109+
var = Var(arg.arg, arg_type)
1110+
var.is_inferred = False
1111+
argument = Argument(var, arg_type, self.visit(default), kind, pos_only)
11101112
argument.set_line(
11111113
arg.lineno,
11121114
arg.col_offset,

mypy/join.py

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -183,55 +183,6 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType:
183183
return best
184184

185185

186-
def join_simple(declaration: Type | None, s: Type, t: Type) -> ProperType:
187-
"""Return a simple least upper bound given the declared type.
188-
189-
This function should be only used by binder, and should not recurse.
190-
For all other uses, use `join_types()`.
191-
"""
192-
declaration = get_proper_type(declaration)
193-
s = get_proper_type(s)
194-
t = get_proper_type(t)
195-
196-
if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false):
197-
# if types are restricted in different ways, use the more general versions
198-
s = mypy.typeops.true_or_false(s)
199-
t = mypy.typeops.true_or_false(t)
200-
201-
if isinstance(s, AnyType):
202-
return s
203-
204-
if isinstance(s, ErasedType):
205-
return t
206-
207-
if is_proper_subtype(s, t, ignore_promotions=True):
208-
return t
209-
210-
if is_proper_subtype(t, s, ignore_promotions=True):
211-
return s
212-
213-
if isinstance(declaration, UnionType):
214-
return mypy.typeops.make_simplified_union([s, t])
215-
216-
if isinstance(s, NoneType) and not isinstance(t, NoneType):
217-
s, t = t, s
218-
219-
if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType):
220-
s, t = t, s
221-
222-
# Meets/joins require callable type normalization.
223-
s, t = normalize_callables(s, t)
224-
225-
if isinstance(s, UnionType) and not isinstance(t, UnionType):
226-
s, t = t, s
227-
228-
value = t.accept(TypeJoinVisitor(s))
229-
if declaration is None or is_subtype(value, declaration):
230-
return value
231-
232-
return declaration
233-
234-
235186
def trivial_join(s: Type, t: Type) -> Type:
236187
"""Return one of types (expanded) if it is a supertype of other, otherwise top type."""
237188
if is_subtype(s, t):

mypy/test/testtypes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from mypy.erasetype import erase_type, remove_instance_last_known_values
99
from mypy.indirection import TypeIndirectionVisitor
10-
from mypy.join import join_simple, join_types
10+
from mypy.join import join_types
1111
from mypy.meet import meet_types, narrow_declared_type
1212
from mypy.nodes import (
1313
ARG_NAMED,
@@ -817,12 +817,12 @@ def test_any_type(self) -> None:
817817
self.assert_join(t, self.fx.anyt, self.fx.anyt)
818818

819819
def test_mixed_truth_restricted_type_simple(self) -> None:
820-
# join_simple against differently restricted truthiness types drops restrictions.
820+
# make_simplified_union against differently restricted truthiness types drops restrictions.
821821
true_a = true_only(self.fx.a)
822822
false_o = false_only(self.fx.o)
823-
j = join_simple(self.fx.o, true_a, false_o)
824-
assert j.can_be_true
825-
assert j.can_be_false
823+
u = make_simplified_union([true_a, false_o])
824+
assert u.can_be_true
825+
assert u.can_be_false
826826

827827
def test_mixed_truth_restricted_type(self) -> None:
828828
# join_types against differently restricted truthiness types drops restrictions.

mypyc/test-data/irbuild-any.test

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def f(a: Any, n: int, c: C) -> None:
3737
c.n = a
3838
a = n
3939
n = a
40-
a.a = n
4140
[out]
4241
def f(a, n, c):
4342
a :: object
@@ -49,10 +48,6 @@ def f(a, n, c):
4948
r3 :: bool
5049
r4 :: object
5150
r5 :: int
52-
r6 :: str
53-
r7 :: object
54-
r8 :: i32
55-
r9 :: bit
5651
L0:
5752
r0 = box(int, n)
5853
c.a = r0; r1 = is_error
@@ -62,10 +57,6 @@ L0:
6257
a = r4
6358
r5 = unbox(int, a)
6459
n = r5
65-
r6 = 'a'
66-
r7 = box(int, n)
67-
r8 = PyObject_SetAttr(a, r6, r7)
68-
r9 = r8 >= 0 :: signed
6960
return 1
7061

7162
[case testCoerceAnyInOps]

test-data/unit/check-classes.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3709,7 +3709,7 @@ def new(uc: Type[U]) -> U:
37093709
if 1:
37103710
u = uc(0)
37113711
u.foo()
3712-
u = uc('') # Error
3712+
uc('') # Error
37133713
u.foo(0) # Error
37143714
return uc()
37153715
u = new(User)

test-data/unit/check-dynamic-typing.test

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ if int():
252252
if int():
253253
a = d.foo(a, a)
254254
d.x = a
255-
d.x.y.z # E: "A" has no attribute "y"
255+
d.x.y.z
256256

257257
class A: pass
258258
[out]
@@ -320,8 +320,10 @@ d = None # All ok
320320
d = t
321321
d = g
322322
d = A
323-
t = d
324-
f = d
323+
324+
d1: Any
325+
t = d1
326+
f = d1
325327
[builtins fixtures/tuple.pyi]
326328

327329

test-data/unit/check-isinstance.test

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ if int():
115115
x = B()
116116
x.z
117117
x = foo()
118-
x.z # E: "A" has no attribute "z"
119-
x.y
118+
reveal_type(x) # N: Revealed type is "Any"
119+
reveal_type(x) # N: Revealed type is "__main__.A"
120120

121121
[case testSingleMultiAssignment]
122122
x = 'a'
@@ -1915,17 +1915,28 @@ if isinstance(x, str, 1): # E: Too many arguments for "isinstance"
19151915
reveal_type(x) # N: Revealed type is "builtins.int"
19161916
[builtins fixtures/isinstancelist.pyi]
19171917

1918-
[case testIsinstanceNarrowAny]
1918+
[case testIsinstanceNarrowAnyExplicit]
19191919
from typing import Any
19201920

19211921
def narrow_any_to_str_then_reassign_to_int() -> None:
1922-
v = 1 # type: Any
1922+
v: Any = 1
19231923

19241924
if isinstance(v, str):
19251925
reveal_type(v) # N: Revealed type is "builtins.str"
19261926
v = 2
19271927
reveal_type(v) # N: Revealed type is "Any"
1928+
[builtins fixtures/isinstance.pyi]
19281929

1930+
[case testIsinstanceNarrowAnyImplicit]
1931+
def foo(): ...
1932+
1933+
def narrow_any_to_str_then_reassign_to_int() -> None:
1934+
v = foo()
1935+
1936+
if isinstance(v, str):
1937+
reveal_type(v) # N: Revealed type is "builtins.str"
1938+
v = 2
1939+
reveal_type(v) # N: Revealed type is "builtins.int"
19291940
[builtins fixtures/isinstance.pyi]
19301941

19311942
[case testNarrowTypeAfterInList]

0 commit comments

Comments
 (0)