Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1ea576c

Browse files
committed
Merge branch 'master' into fix/narrow_nonetype
# Conflicts: # test-data/unit/check-narrowing.test
2 parents 05f2c71 + cda163d commit 1ea576c

42 files changed

Lines changed: 1835 additions & 436 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ jobs:
133133
./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV
134134
source $VENV/bin/activate
135135
- name: Install tox
136-
run: pip install --upgrade 'setuptools!=50' tox==4.11.0
136+
run: pip install setuptools==68.2.2 tox==4.11.0
137137
- name: Compiled with mypyc
138138
if: ${{ matrix.test_mypyc }}
139139
run: |
@@ -185,7 +185,7 @@ jobs:
185185
default: 3.11.1
186186
command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');"
187187
- name: Install tox
188-
run: pip install --upgrade 'setuptools!=50' tox==4.11.0
188+
run: pip install setuptools==68.2.2 tox==4.11.0
189189
- name: Setup tox environment
190190
run: tox run -e py --notest
191191
- name: Test

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ graft mypyc/doc
3131
# files necessary for testing sdist
3232
include mypy-requirements.txt
3333
include build-requirements.txt
34+
include test-requirements.in
3435
include test-requirements.txt
3536
include mypy_self_check.ini
3637
prune misc

mypy/binder.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@
1212
from mypy.subtypes import is_same_type, is_subtype
1313
from mypy.types import (
1414
AnyType,
15+
Instance,
1516
NoneType,
1617
PartialType,
18+
ProperType,
19+
TupleType,
1720
Type,
1821
TypeOfAny,
1922
TypeType,
2023
UnionType,
24+
UnpackType,
25+
find_unpack_in_list,
2126
get_proper_type,
2227
)
2328
from mypy.typevars import fill_typevars_with_any
@@ -213,6 +218,24 @@ def update_from_options(self, frames: list[Frame]) -> bool:
213218
for other in resulting_values[1:]:
214219
assert other is not None
215220
type = join_simple(self.declarations[key], type, other)
221+
# Try simplifying resulting type for unions involving variadic tuples.
222+
# Technically, everything is still valid without this step, but if we do
223+
# not do this, this may create long unions after exiting an if check like:
224+
# x: tuple[int, ...]
225+
# if len(x) < 10:
226+
# ...
227+
# We want the type of x to be tuple[int, ...] after this block (if it is
228+
# still equivalent to such type).
229+
if isinstance(type, UnionType):
230+
type = collapse_variadic_union(type)
231+
if isinstance(type, ProperType) and isinstance(type, UnionType):
232+
# Simplify away any extra Any's that were added to the declared
233+
# type when popping a frame.
234+
simplified = UnionType.make_union(
235+
[t for t in type.items if not isinstance(get_proper_type(t), AnyType)]
236+
)
237+
if simplified == self.declarations[key]:
238+
type = simplified
216239
if current_value is None or not is_same_type(type, current_value):
217240
self._put(key, type)
218241
changed = True
@@ -453,3 +476,63 @@ def get_declaration(expr: BindableExpression) -> Type | None:
453476
elif isinstance(expr.node, TypeInfo):
454477
return TypeType(fill_typevars_with_any(expr.node))
455478
return None
479+
480+
481+
def collapse_variadic_union(typ: UnionType) -> Type:
482+
"""Simplify a union involving variadic tuple if possible.
483+
484+
This will collapse a type like e.g.
485+
tuple[X, Z] | tuple[X, Y, Z] | tuple[X, Y, Y, *tuple[Y, ...], Z]
486+
back to
487+
tuple[X, *tuple[Y, ...], Z]
488+
which is equivalent, but much simpler form of the same type.
489+
"""
490+
tuple_items = []
491+
other_items = []
492+
for t in typ.items:
493+
p_t = get_proper_type(t)
494+
if isinstance(p_t, TupleType):
495+
tuple_items.append(p_t)
496+
else:
497+
other_items.append(t)
498+
if len(tuple_items) <= 1:
499+
# This type cannot be simplified further.
500+
return typ
501+
tuple_items = sorted(tuple_items, key=lambda t: len(t.items))
502+
first = tuple_items[0]
503+
last = tuple_items[-1]
504+
unpack_index = find_unpack_in_list(last.items)
505+
if unpack_index is None:
506+
return typ
507+
unpack = last.items[unpack_index]
508+
assert isinstance(unpack, UnpackType)
509+
unpacked = get_proper_type(unpack.type)
510+
if not isinstance(unpacked, Instance):
511+
return typ
512+
assert unpacked.type.fullname == "builtins.tuple"
513+
suffix = last.items[unpack_index + 1 :]
514+
515+
# Check that first item matches the expected pattern and infer prefix.
516+
if len(first.items) < len(suffix):
517+
return typ
518+
if suffix and first.items[-len(suffix) :] != suffix:
519+
return typ
520+
if suffix:
521+
prefix = first.items[: -len(suffix)]
522+
else:
523+
prefix = first.items
524+
525+
# Check that all middle types match the expected pattern as well.
526+
arg = unpacked.args[0]
527+
for i, it in enumerate(tuple_items[1:-1]):
528+
if it.items != prefix + [arg] * (i + 1) + suffix:
529+
return typ
530+
531+
# Check the last item (the one with unpack), and choose an appropriate simplified type.
532+
if last.items != prefix + [arg] * (len(typ.items) - 1) + [unpack] + suffix:
533+
return typ
534+
if len(first.items) == 0:
535+
simplified: Type = unpacked.copy_modified()
536+
else:
537+
simplified = TupleType(prefix + [unpack] + suffix, fallback=last.partial_fallback)
538+
return UnionType.make_union([simplified] + other_items)

mypy/build.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,7 +1991,7 @@ def __init__(
19911991
raise ModuleNotFound
19921992

19931993
# Parse the file (and then some) to get the dependencies.
1994-
self.parse_file()
1994+
self.parse_file(temporary=temporary)
19951995
self.compute_dependencies()
19961996

19971997
@property
@@ -2109,7 +2109,7 @@ def fix_cross_refs(self) -> None:
21092109

21102110
# Methods for processing modules from source code.
21112111

2112-
def parse_file(self) -> None:
2112+
def parse_file(self, *, temporary: bool = False) -> None:
21132113
"""Parse file and run first pass of semantic analysis.
21142114
21152115
Everything done here is local to the file. Don't depend on imported
@@ -2194,12 +2194,14 @@ def parse_file(self) -> None:
21942194
else:
21952195
self.early_errors = manager.ast_cache[self.id][1]
21962196

2197-
modules[self.id] = self.tree
2197+
if not temporary:
2198+
modules[self.id] = self.tree
21982199

21992200
if not cached:
22002201
self.semantic_analysis_pass1()
22012202

2202-
self.check_blockers()
2203+
if not temporary:
2204+
self.check_blockers()
22032205

22042206
manager.ast_cache[self.id] = (self.tree, self.early_errors)
22052207

0 commit comments

Comments
 (0)