Skip to content

Commit 7029712

Browse files
authored
Fix deferral logic for special types (#20678)
Fixes #20671 The crash was caused by extending a TypedDict that was itself not ready (contained placeholders). While looking for a fix I noticed that for some reason we check for placeholders in _previous_ version of a TypedDict etc. Not sure why, this mat have been a remainder of an old hack. Replacing it with a check for placeholders in current version looks more correct (although there is probably no difference for other special types) and turns out to be a fix for this issue.
1 parent bfe5024 commit 7029712

File tree

4 files changed

+31
-3
lines changed

4 files changed

+31
-3
lines changed

mypy/semanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2688,7 +2688,7 @@ def configure_tuple_base_class(self, defn: ClassDef, base: TupleType) -> Instanc
26882688
if info.tuple_type and info.tuple_type != base and not has_placeholder(info.tuple_type):
26892689
self.fail("Class has two incompatible bases derived from tuple", defn)
26902690
defn.has_incompatible_baseclass = True
2691-
if info.special_alias and has_placeholder(info.special_alias.target):
2691+
if has_placeholder(base):
26922692
self.process_placeholder(
26932693
None, "tuple base", defn, force_progress=base != info.tuple_type
26942694
)

mypy/semanal_namedtuple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ def build_namedtuple_typeinfo(
527527
info = existing_info or self.api.basic_new_typeinfo(name, fallback, line)
528528
info.is_named_tuple = True
529529
tuple_base = TupleType(types, fallback)
530-
if info.special_alias and has_placeholder(info.special_alias.target):
530+
if has_placeholder(tuple_base):
531531
self.api.process_placeholder(
532532
None, "NamedTuple item", info, force_progress=tuple_base != info.tuple_type
533533
)

mypy/semanal_typeddict.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ def build_typeddict_typeinfo(
607607
assert fallback is not None
608608
info = existing_info or self.api.basic_new_typeinfo(name, fallback, line)
609609
typeddict_type = TypedDictType(item_types, required_keys, readonly_keys, fallback)
610-
if info.special_alias and has_placeholder(info.special_alias.target):
610+
if has_placeholder(typeddict_type):
611611
self.api.process_placeholder(
612612
None, "TypedDict item", info, force_progress=typeddict_type != info.typeddict_type
613613
)

test-data/unit/check-typeddict.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4581,3 +4581,31 @@ bad({"x": "foo"}) # E: Dict entry 0 has incompatible type "str": "str"; expecte
45814581

45824582
[builtins fixtures/dict.pyi]
45834583
[typing fixtures/typing-typeddict.pyi]
4584+
4585+
[case testTypedDictUnpackImportCycle]
4586+
import mre
4587+
4588+
[file mre.py]
4589+
from mre_pkg.model import Callback
4590+
Callback(callback=1) # E: Argument "callback" to "Callback" has incompatible type "int"; expected "Callback"
4591+
4592+
[file mre_pkg/model.pyi]
4593+
from typing_extensions import Unpack, TYPE_CHECKING, TypedDict
4594+
from .callbacks import Callback as Callback
4595+
4596+
class _ModelInit(TypedDict, total=False):
4597+
callback: Callback
4598+
4599+
class Model:
4600+
def __init__(self, **kwargs: Unpack[_ModelInit]) -> None: ...
4601+
4602+
[file mre_pkg/callbacks.pyi]
4603+
from typing_extensions import Unpack, TYPE_CHECKING, TypedDict
4604+
from .model import Model, _ModelInit
4605+
4606+
class _CallbackInit(_ModelInit, total=False): ...
4607+
4608+
class Callback(Model):
4609+
def __init__(self, **kwargs: Unpack[_CallbackInit]) -> None: ...
4610+
[builtins fixtures/dict.pyi]
4611+
[typing fixtures/typing-typeddict.pyi]

0 commit comments

Comments
 (0)