Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import mypy.errorcodes as codes
from mypy import applytype, erasetype, join, message_registry, nodes, operators, types
from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals
from mypy.checkmember import analyze_member_access, typeddict_callable
from mypy.checkmember import analyze_member_access
from mypy.checkstrformat import StringFormatterChecker
from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
from mypy.errors import ErrorWatcher, report_internal_error
Expand Down Expand Up @@ -957,7 +957,20 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType:
Note it is not safe to move this to type_object_type() since it will crash
on plugin-generated TypedDicts, that may not have the special_alias.
"""
return typeddict_callable(info, self.named_type)
assert info.special_alias is not None
target = info.special_alias.target
assert isinstance(target, ProperType) and isinstance(target, TypedDictType)
expected_types = list(target.items.values())
kinds = [ArgKind.ARG_NAMED] * len(expected_types)
names = list(target.items.keys())
return CallableType(
expected_types,
kinds,
names,
target,
self.named_type("builtins.type"),
variables=info.defn.type_vars,
)

def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType:
return CallableType(
Expand Down
28 changes: 1 addition & 27 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
ARG_STAR,
EXCLUDED_ENUM_ATTRIBUTES,
SYMBOL_FUNCBASE_TYPES,
ArgKind,
Context,
Decorator,
FuncBase,
Expand Down Expand Up @@ -1094,7 +1093,7 @@ def analyze_class_attribute_access(
if isinstance(node.node, TypeInfo):
if node.node.typeddict_type:
# We special-case TypedDict, because they don't define any constructor.
return typeddict_callable(node.node, mx.named_type)
return mx.chk.expr_checker.typeddict_callable(node.node)
elif node.node.fullname == "types.NoneType":
# We special case NoneType, because its stub definition is not related to None.
return TypeType(NoneType())
Expand Down Expand Up @@ -1280,31 +1279,6 @@ class B(A[str]): pass
return t


def typeddict_callable(info: TypeInfo, named_type: Callable[[str], Instance]) -> CallableType:
"""Construct a reasonable type for a TypedDict type in runtime context.

If it appears as a callee, it will be special-cased anyway, e.g. it is
also allowed to accept a single positional argument if it is a dict literal.

Note it is not safe to move this to type_object_type() since it will crash
on plugin-generated TypedDicts, that may not have the special_alias.
"""
assert info.special_alias is not None
target = info.special_alias.target
assert isinstance(target, ProperType) and isinstance(target, TypedDictType)
expected_types = list(target.items.values())
kinds = [ArgKind.ARG_NAMED] * len(expected_types)
names = list(target.items.keys())
return CallableType(
expected_types,
kinds,
names,
target,
named_type("builtins.type"),
variables=info.defn.type_vars,
)


def analyze_decorator_or_funcbase_access(
defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext
) -> Type:
Expand Down