55
66import mypy .errorcodes as codes
77from mypy import message_registry
8- from mypy .nodes import DictExpr , IntExpr , StrExpr , UnaryExpr
8+ from mypy .nodes import IntExpr , StrExpr , UnaryExpr
99from mypy .plugin import (
1010 AttributeContext ,
1111 ClassDefContext ,
7575 TypedDictType ,
7676 TypeOfAny ,
7777 TypeVarType ,
78+ UninhabitedType ,
7879 UnionType ,
7980 get_proper_type ,
8081 get_proper_types ,
@@ -120,9 +121,9 @@ def get_function_signature_hook(
120121 def get_method_signature_hook (
121122 self , fullname : str
122123 ) -> Callable [[MethodSigContext ], FunctionLike ] | None :
123- if fullname == "typing.Mapping. get" :
124- return typed_dict_get_signature_callback
125- elif fullname in TD_SETDEFAULT_NAMES :
124+ # NOTE: signatures for `__setitem__`, `__delitem__` and ` get` are
125+ # defined in checkmember.py/analyze_typeddict_access
126+ if fullname in TD_SETDEFAULT_NAMES :
126127 return typed_dict_setdefault_signature_callback
127128 elif fullname in TD_POP_NAMES :
128129 return typed_dict_pop_signature_callback
@@ -212,46 +213,6 @@ def get_class_decorator_hook_2(
212213 return None
213214
214215
215- def typed_dict_get_signature_callback (ctx : MethodSigContext ) -> CallableType :
216- """Try to infer a better signature type for TypedDict.get.
217-
218- This is used to get better type context for the second argument that
219- depends on a TypedDict value type.
220- """
221- signature = ctx .default_signature
222- if (
223- isinstance (ctx .type , TypedDictType )
224- and len (ctx .args ) == 2
225- and len (ctx .args [0 ]) == 1
226- and isinstance (ctx .args [0 ][0 ], StrExpr )
227- and len (signature .arg_types ) == 2
228- and len (signature .variables ) == 1
229- and len (ctx .args [1 ]) == 1
230- ):
231- key = ctx .args [0 ][0 ].value
232- value_type = get_proper_type (ctx .type .items .get (key ))
233- ret_type = signature .ret_type
234- if value_type :
235- default_arg = ctx .args [1 ][0 ]
236- if (
237- isinstance (value_type , TypedDictType )
238- and isinstance (default_arg , DictExpr )
239- and len (default_arg .items ) == 0
240- ):
241- # Caller has empty dict {} as default for typed dict.
242- value_type = value_type .copy_modified (required_keys = set ())
243- # Tweak the signature to include the value type as context. It's
244- # only needed for type inference since there's a union with a type
245- # variable that accepts everything.
246- tv = signature .variables [0 ]
247- assert isinstance (tv , TypeVarType )
248- return signature .copy_modified (
249- arg_types = [signature .arg_types [0 ], make_simplified_union ([value_type , tv ])],
250- ret_type = ret_type ,
251- )
252- return signature
253-
254-
255216def typed_dict_get_callback (ctx : MethodContext ) -> Type :
256217 """Infer a precise return type for TypedDict.get with literal first argument."""
257218 if (
@@ -263,30 +224,41 @@ def typed_dict_get_callback(ctx: MethodContext) -> Type:
263224 if keys is None :
264225 return ctx .default_return_type
265226
227+ default_type : Type
228+ if len (ctx .arg_types ) <= 1 or not ctx .arg_types [1 ]:
229+ default_type = NoneType ()
230+ elif len (ctx .arg_types [1 ]) == 1 and len (ctx .args [1 ]) == 1 :
231+ default_type = ctx .arg_types [1 ][0 ]
232+ else :
233+ return ctx .default_return_type
234+
266235 output_types : list [Type ] = []
267236 for key in keys :
268- value_type = get_proper_type ( ctx .type .items .get (key ) )
237+ value_type : Type | None = ctx .type .items .get (key )
269238 if value_type is None :
270239 return ctx .default_return_type
271240
272- if len ( ctx . arg_types ) == 1 :
241+ if key in ctx . type . required_keys :
273242 output_types .append (value_type )
274- elif len (ctx .arg_types ) == 2 and len (ctx .arg_types [1 ]) == 1 and len (ctx .args [1 ]) == 1 :
275- default_arg = ctx .args [1 ][0 ]
243+ else :
244+ # HACK to deal with get(key, {})
245+ proper_default = get_proper_type (default_type )
276246 if (
277- isinstance (default_arg , DictExpr )
278- and len (default_arg .items ) == 0
279- and isinstance (value_type , TypedDictType )
247+ isinstance (vt := get_proper_type (value_type ), TypedDictType )
248+ and isinstance (proper_default , Instance )
249+ and proper_default .type .fullname == "builtins.dict"
250+ and len (proper_default .args ) == 2
251+ and isinstance (get_proper_type (proper_default .args [0 ]), UninhabitedType )
252+ and isinstance (get_proper_type (proper_default .args [1 ]), UninhabitedType )
280253 ):
281- # Special case '{}' as the default for a typed dict type.
282- output_types .append (value_type .copy_modified (required_keys = set ()))
254+ output_types .append (vt .copy_modified (required_keys = set ()))
283255 else :
284256 output_types .append (value_type )
285- output_types .append (ctx .arg_types [1 ][0 ])
286-
287- if len (ctx .arg_types ) == 1 :
288- output_types .append (NoneType ())
257+ output_types .append (default_type )
289258
259+ # for nicer reveal_type, put default at the end, if it is present
260+ if default_type in output_types :
261+ output_types = [t for t in output_types if t != default_type ] + [default_type ]
290262 return make_simplified_union (output_types )
291263 return ctx .default_return_type
292264
0 commit comments