Skip to content

Commit 7ab1ede

Browse files
authored
Merge branch 'master' into bugfix/gh-19346-assert-import
2 parents 22300fc + 07d4a1b commit 7ab1ede

Some content is hidden

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

54 files changed

+1072
-237
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ Related PRs:
170170
* Enable ANSI color codes for dmypy client in Windows (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088))
171171
* Extend special case for context-based type variable inference to unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976))
172172

173+
### Mypy 1.17.1
174+
* Retain `None` as constraints bottom if no bottoms were provided (Stanislav Terliakov, PR [19485](https://github.com/python/mypy/pull/19485))
175+
* Fix "ignored exception in `hasattr`" in dmypy (Stanislav Terliakov, PR [19428](https://github.com/python/mypy/pull/19428))
176+
* Prevent a crash when InitVar is redefined with a method in a subclass (Stanislav Terliakov, PR [19453](https://github.com/python/mypy/pull/19453))
177+
173178
### Acknowledgements
174179

175180
Thanks to all mypy contributors who contributed to this release:

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,9 @@
278278
intersphinx_mapping = {
279279
"python": ("https://docs.python.org/3", None),
280280
"attrs": ("https://www.attrs.org/en/stable/", None),
281-
"cython": ("https://docs.cython.org/en/latest", None),
281+
"cython": ("https://cython.readthedocs.io/en/stable", None),
282282
"monkeytype": ("https://monkeytype.readthedocs.io/en/latest", None),
283-
"setuptools": ("https://setuptools.readthedocs.io/en/latest", None),
283+
"setuptools": ("https://setuptools.pypa.io/en/latest", None),
284284
}
285285

286286

mypy/binder.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ def __init__(self, options: Options) -> None:
138138
# flexible inference of variable types (--allow-redefinition-new).
139139
self.bind_all = options.allow_redefinition_new
140140

141+
# This tracks any externally visible changes in binder to invalidate
142+
# expression caches when needed.
143+
self.version = 0
144+
141145
def _get_id(self) -> int:
142146
self.next_id += 1
143147
return self.next_id
@@ -158,6 +162,7 @@ def push_frame(self, conditional_frame: bool = False) -> Frame:
158162
return f
159163

160164
def _put(self, key: Key, type: Type, from_assignment: bool, index: int = -1) -> None:
165+
self.version += 1
161166
self.frames[index].types[key] = CurrentType(type, from_assignment)
162167

163168
def _get(self, key: Key, index: int = -1) -> CurrentType | None:
@@ -185,6 +190,7 @@ def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> N
185190
self._put(key, typ, from_assignment)
186191

187192
def unreachable(self) -> None:
193+
self.version += 1
188194
self.frames[-1].unreachable = True
189195

190196
def suppress_unreachable_warnings(self) -> None:

mypy/build.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def default_flush_errors(
194194
result.errors = messages
195195
return result
196196
except CompileError as e:
197-
# CompileErrors raised from an errors object carry all of the
197+
# CompileErrors raised from an errors object carry all the
198198
# messages that have not been reported out by error streaming.
199199
# Patch it up to contain either none or all none of the messages,
200200
# depending on whether we are flushing errors.
@@ -802,11 +802,11 @@ def correct_rel_imp(imp: ImportFrom | ImportAll) -> str:
802802
res.append((pri, sub_id, imp.line))
803803
else:
804804
all_are_submodules = False
805-
# Add cur_id as a dependency, even if all of the
805+
# Add cur_id as a dependency, even if all the
806806
# imports are submodules. Processing import from will try
807807
# to look through cur_id, so we should depend on it.
808-
# As a workaround for for some bugs in cycle handling (#4498),
809-
# if all of the imports are submodules, do the import at a lower
808+
# As a workaround for some bugs in cycle handling (#4498),
809+
# if all the imports are submodules, do the import at a lower
810810
# priority.
811811
pri = import_priority(imp, PRI_HIGH if not all_are_submodules else PRI_LOW)
812812
res.append((pri, cur_id, imp.line))
@@ -929,7 +929,7 @@ def write_deps_cache(
929929
) -> None:
930930
"""Write cache files for fine-grained dependencies.
931931
932-
Serialize fine-grained dependencies map for fine grained mode.
932+
Serialize fine-grained dependencies map for fine-grained mode.
933933
934934
Dependencies on some module 'm' is stored in the dependency cache
935935
file m.deps.json. This entails some spooky action at a distance:
@@ -943,7 +943,7 @@ def write_deps_cache(
943943
fine-grained dependencies in a global cache file:
944944
* We take a snapshot of current sources to later check consistency
945945
between the fine-grained dependency cache and module cache metadata
946-
* We store the mtime of all of the dependency files to verify they
946+
* We store the mtime of all the dependency files to verify they
947947
haven't changed
948948
"""
949949
metastore = manager.metastore
@@ -1111,7 +1111,7 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta]
11111111
if deps_meta is None:
11121112
return None
11131113
meta_snapshot = deps_meta["snapshot"]
1114-
# Take a snapshot of the source hashes from all of the metas we found.
1114+
# Take a snapshot of the source hashes from all the metas we found.
11151115
# (Including the ones we rejected because they were out of date.)
11161116
# We use this to verify that they match up with the proto_deps.
11171117
current_meta_snapshot = {

mypy/checker.py

Lines changed: 82 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ def __init__(
397397
self.is_stub = tree.is_stub
398398
self.is_typeshed_stub = tree.is_typeshed_file(options)
399399
self.inferred_attribute_types = None
400+
self.allow_constructor_cache = True
400401

401402
# If True, process function definitions. If False, don't. This is used
402403
# for processing module top levels in fine-grained incremental mode.
@@ -448,7 +449,6 @@ def reset(self) -> None:
448449
self.binder = ConditionalTypeBinder(self.options)
449450
self._type_maps[1:] = []
450451
self._type_maps[0].clear()
451-
self.temp_type_map = None
452452
self.expr_checker.reset()
453453
self.deferred_nodes = []
454454
self.partial_types = []
@@ -500,12 +500,16 @@ def check_first_pass(self) -> None:
500500
)
501501

502502
def check_second_pass(
503-
self, todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None
503+
self,
504+
todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None,
505+
*,
506+
allow_constructor_cache: bool = True,
504507
) -> bool:
505508
"""Run second or following pass of type checking.
506509
507510
This goes through deferred nodes, returning True if there were any.
508511
"""
512+
self.allow_constructor_cache = allow_constructor_cache
509513
self.recurse_into_functions = True
510514
with state.strict_optional_set(self.options.strict_optional), checker_state.set(self):
511515
if not todo and not self.deferred_nodes:
@@ -1369,49 +1373,19 @@ def check_func_def(
13691373
)
13701374

13711375
# Store argument types.
1376+
found_self = False
1377+
if isinstance(defn, FuncDef) and not defn.is_decorated:
1378+
found_self = self.require_correct_self_argument(typ, defn)
13721379
for i in range(len(typ.arg_types)):
13731380
arg_type = typ.arg_types[i]
1374-
if (
1375-
isinstance(defn, FuncDef)
1376-
and ref_type is not None
1377-
and i == 0
1378-
and defn.has_self_or_cls_argument
1379-
and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]
1380-
):
1381-
if defn.is_class or defn.name == "__new__":
1382-
ref_type = mypy.types.TypeType.make_normalized(ref_type)
1383-
if not is_same_type(arg_type, ref_type):
1384-
# This level of erasure matches the one in checkmember.check_self_arg(),
1385-
# better keep these two checks consistent.
1386-
erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
1387-
if not is_subtype(ref_type, erased, ignore_type_params=True):
1388-
if (
1389-
isinstance(erased, Instance)
1390-
and erased.type.is_protocol
1391-
or isinstance(erased, TypeType)
1392-
and isinstance(erased.item, Instance)
1393-
and erased.item.type.is_protocol
1394-
):
1395-
# We allow the explicit self-type to be not a supertype of
1396-
# the current class if it is a protocol. For such cases
1397-
# the consistency check will be performed at call sites.
1398-
msg = None
1399-
elif typ.arg_names[i] in {"self", "cls"}:
1400-
msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
1401-
erased.str_with_options(self.options),
1402-
ref_type.str_with_options(self.options),
1403-
)
1404-
else:
1405-
msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
1406-
if msg:
1407-
self.fail(msg, defn)
1408-
elif isinstance(arg_type, TypeVarType):
1381+
if isinstance(arg_type, TypeVarType):
14091382
# Refuse covariant parameter type variables
14101383
# TODO: check recursively for inner type variables
14111384
if (
14121385
arg_type.variance == COVARIANT
14131386
and defn.name not in ("__init__", "__new__", "__post_init__")
14141387
and not is_private(defn.name) # private methods are not inherited
1388+
and (i != 0 or not found_self)
14151389
):
14161390
ctx: Context = arg_type
14171391
if ctx.line < 0:
@@ -1561,6 +1535,69 @@ def check_func_def(
15611535

15621536
self.binder = old_binder
15631537

1538+
def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool:
1539+
func = get_proper_type(func)
1540+
if not isinstance(func, CallableType):
1541+
return False
1542+
1543+
# Do not report errors for untyped methods in classes nested in untyped funcs.
1544+
if not (
1545+
self.options.check_untyped_defs
1546+
or len(self.dynamic_funcs) < 2
1547+
or not self.dynamic_funcs[-2]
1548+
or not defn.is_dynamic()
1549+
):
1550+
return bool(func.arg_types)
1551+
1552+
with self.scope.push_function(defn):
1553+
# We temporary push the definition to get the self type as
1554+
# visible from *inside* of this function/method.
1555+
ref_type: Type | None = self.scope.active_self_type()
1556+
if ref_type is None:
1557+
return False
1558+
1559+
if not defn.has_self_or_cls_argument or (
1560+
func.arg_kinds and func.arg_kinds[0] in [nodes.ARG_STAR, nodes.ARG_STAR2]
1561+
):
1562+
return False
1563+
1564+
if not func.arg_types:
1565+
self.fail(
1566+
'Method must have at least one argument. Did you forget the "self" argument?', defn
1567+
)
1568+
return False
1569+
1570+
arg_type = func.arg_types[0]
1571+
if defn.is_class or defn.name == "__new__":
1572+
ref_type = mypy.types.TypeType.make_normalized(ref_type)
1573+
if is_same_type(arg_type, ref_type):
1574+
return True
1575+
1576+
# This level of erasure matches the one in checkmember.check_self_arg(),
1577+
# better keep these two checks consistent.
1578+
erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
1579+
if not is_subtype(ref_type, erased, ignore_type_params=True):
1580+
if (
1581+
isinstance(erased, Instance)
1582+
and erased.type.is_protocol
1583+
or isinstance(erased, TypeType)
1584+
and isinstance(erased.item, Instance)
1585+
and erased.item.type.is_protocol
1586+
):
1587+
# We allow the explicit self-type to be not a supertype of
1588+
# the current class if it is a protocol. For such cases
1589+
# the consistency check will be performed at call sites.
1590+
msg = None
1591+
elif func.arg_names[0] in {"self", "cls"}:
1592+
msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
1593+
erased.str_with_options(self.options), ref_type.str_with_options(self.options)
1594+
)
1595+
else:
1596+
msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
1597+
if msg:
1598+
self.fail(msg, defn)
1599+
return True
1600+
15641601
def is_var_redefined_in_outer_context(self, v: Var, after_line: int) -> bool:
15651602
"""Can the variable be assigned to at module top level or outer function?
15661603
@@ -3019,6 +3056,8 @@ def visit_block(self, b: Block) -> None:
30193056
break
30203057
else:
30213058
self.accept(s)
3059+
# Clear expression cache after each statement to avoid unlimited growth.
3060+
self.expr_checker.expr_cache.clear()
30223061

30233062
def should_report_unreachable_issues(self) -> bool:
30243063
return (
@@ -4000,7 +4039,7 @@ def check_multi_assignment_from_union(
40004039
for t, lv in zip(transposed, self.flatten_lvalues(lvalues)):
40014040
# We can access _type_maps directly since temporary type maps are
40024041
# only created within expressions.
4003-
t.append(self._type_maps[0].pop(lv, AnyType(TypeOfAny.special_form)))
4042+
t.append(self._type_maps[-1].pop(lv, AnyType(TypeOfAny.special_form)))
40044043
union_types = tuple(make_simplified_union(col) for col in transposed)
40054044
for expr, items in assignments.items():
40064045
# Bind a union of types collected in 'assignments' to every expression.
@@ -4659,6 +4698,8 @@ def replace_partial_type(
46594698
) -> None:
46604699
"""Replace the partial type of var with a non-partial type."""
46614700
var.type = new_type
4701+
# Updating a partial type should invalidate expression caches.
4702+
self.binder.version += 1
46624703
del partial_types[var]
46634704
if self.options.allow_redefinition_new:
46644705
# When using --allow-redefinition-new, binder tracks all types of
@@ -5298,6 +5339,7 @@ def visit_decorator_inner(
52985339
)
52995340
if non_trivial_decorator:
53005341
self.check_untyped_after_decorator(sig, e.func)
5342+
self.require_correct_self_argument(sig, e.func)
53015343
sig = set_callable_name(sig, e.func)
53025344
e.var.type = sig
53035345
e.var.is_ready = True
@@ -5577,6 +5619,8 @@ def infer_variable_types_from_type_maps(
55775619
previous_type, _, _ = self.check_lvalue(expr)
55785620
if previous_type is not None:
55795621
already_exists = True
5622+
if isinstance(expr.node, Var) and expr.node.is_final:
5623+
self.msg.cant_assign_to_final(expr.name, False, expr)
55805624
if self.check_subtype(
55815625
typ,
55825626
previous_type,

mypy/checker_shared.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class TypeCheckerSharedApi(CheckerPluginInterface):
137137
module_refs: set[str]
138138
scope: CheckerScope
139139
checking_missing_await: bool
140+
allow_constructor_cache: bool
140141

141142
@property
142143
@abstractmethod

0 commit comments

Comments
 (0)