Skip to content

Commit c6cf7cd

Browse files
authored
Use tuple[object, ...] and dict[str, object] as upper bounds for ParamSpec.args and ParamSpec.kwargs (#12668)
Mypy thought that a variable annotated with P.args is not iterable, and that a variable annotated with P.kwargs does not have a .pop() method. Fixes #12386.
1 parent d48d548 commit c6cf7cd

File tree

5 files changed

+186
-47
lines changed

5 files changed

+186
-47
lines changed

mypy/checkmember.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def _analyze_member_access(name: str,
164164
return analyze_typeddict_access(name, typ, mx, override_info)
165165
elif isinstance(typ, NoneType):
166166
return analyze_none_member_access(name, typ, mx)
167-
elif isinstance(typ, TypeVarType):
167+
elif isinstance(typ, TypeVarLikeType):
168168
return _analyze_member_access(name, typ.upper_bound, mx, override_info)
169169
elif isinstance(typ, DeletedType):
170170
mx.msg.deleted_as_rvalue(typ, mx.context)

mypy/semanal_shared.py

+47-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
from abc import abstractmethod
44

5-
from typing import Optional, List, Callable
6-
from typing_extensions import Final
5+
from typing import Optional, List, Callable, Union
6+
from typing_extensions import Final, Protocol
77
from mypy_extensions import trait
88

99
from mypy.nodes import (
1010
Context, SymbolTableNode, FuncDef, Node, TypeInfo, Expression,
1111
SymbolNode, SymbolTable
1212
)
1313
from mypy.types import (
14-
Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type
14+
Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type,
15+
ParamSpecType, ParamSpecFlavor, Parameters, TypeVarId
1516
)
1617
from mypy.tvar_scope import TypeVarLikeScope
1718
from mypy.errorcodes import ErrorCode
@@ -212,3 +213,46 @@ def calculate_tuple_fallback(typ: TupleType) -> None:
212213
fallback = typ.partial_fallback
213214
assert fallback.type.fullname == 'builtins.tuple'
214215
fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:]
216+
217+
218+
class _NamedTypeCallback(Protocol):
219+
def __call__(
220+
self, fully_qualified_name: str, args: Optional[List[Type]] = None
221+
) -> Instance: ...
222+
223+
224+
def paramspec_args(
225+
name: str, fullname: str, id: Union[TypeVarId, int], *,
226+
named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1,
227+
prefix: Optional[Parameters] = None
228+
) -> ParamSpecType:
229+
return ParamSpecType(
230+
name,
231+
fullname,
232+
id,
233+
flavor=ParamSpecFlavor.ARGS,
234+
upper_bound=named_type_func('builtins.tuple', [named_type_func('builtins.object')]),
235+
line=line,
236+
column=column,
237+
prefix=prefix
238+
)
239+
240+
241+
def paramspec_kwargs(
242+
name: str, fullname: str, id: Union[TypeVarId, int], *,
243+
named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1,
244+
prefix: Optional[Parameters] = None
245+
) -> ParamSpecType:
246+
return ParamSpecType(
247+
name,
248+
fullname,
249+
id,
250+
flavor=ParamSpecFlavor.KWARGS,
251+
upper_bound=named_type_func(
252+
'builtins.dict',
253+
[named_type_func('builtins.str'), named_type_func('builtins.object')]
254+
),
255+
line=line,
256+
column=column,
257+
prefix=prefix
258+
)

mypy/typeanal.py

+13-17
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from mypy.tvar_scope import TypeVarLikeScope
3434
from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError
3535
from mypy.plugin import Plugin, TypeAnalyzerPluginInterface, AnalyzeTypeContext
36-
from mypy.semanal_shared import SemanticAnalyzerCoreInterface
36+
from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs
3737
from mypy.errorcodes import ErrorCode
3838
from mypy import nodes, message_registry, errorcodes as codes
3939

@@ -711,13 +711,13 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type:
711711
tvar_def = self.tvar_scope.get_binding(sym)
712712
if isinstance(tvar_def, ParamSpecType):
713713
if kind == ARG_STAR:
714-
flavor = ParamSpecFlavor.ARGS
714+
make_paramspec = paramspec_args
715715
elif kind == ARG_STAR2:
716-
flavor = ParamSpecFlavor.KWARGS
716+
make_paramspec = paramspec_kwargs
717717
else:
718718
assert False, kind
719-
return ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, flavor,
720-
upper_bound=self.named_type('builtins.object'),
719+
return make_paramspec(tvar_def.name, tvar_def.fullname, tvar_def.id,
720+
named_type_func=self.named_type,
721721
line=t.line, column=t.column)
722722
return self.anal_type(t, nested=nested)
723723

@@ -855,13 +855,11 @@ def analyze_callable_args_for_paramspec(
855855
if not isinstance(tvar_def, ParamSpecType):
856856
return None
857857

858-
# TODO: Use tuple[...] or Mapping[..] instead?
859-
obj = self.named_type('builtins.object')
860858
return CallableType(
861-
[ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS,
862-
upper_bound=obj),
863-
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS,
864-
upper_bound=obj)],
859+
[paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id,
860+
named_type_func=self.named_type),
861+
paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id,
862+
named_type_func=self.named_type)],
865863
[nodes.ARG_STAR, nodes.ARG_STAR2],
866864
[None, None],
867865
ret_type=ret_type,
@@ -891,18 +889,16 @@ def analyze_callable_args_for_concatenate(
891889
if not isinstance(tvar_def, ParamSpecType):
892890
return None
893891

894-
# TODO: Use tuple[...] or Mapping[..] instead?
895-
obj = self.named_type('builtins.object')
896892
# ick, CallableType should take ParamSpecType
897893
prefix = tvar_def.prefix
898894
# we don't set the prefix here as generic arguments will get updated at some point
899895
# in the future. CallableType.param_spec() accounts for this.
900896
return CallableType(
901897
[*prefix.arg_types,
902-
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS,
903-
upper_bound=obj),
904-
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS,
905-
upper_bound=obj)],
898+
paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id,
899+
named_type_func=self.named_type),
900+
paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id,
901+
named_type_func=self.named_type)],
906902
[*prefix.arg_kinds, nodes.ARG_STAR, nodes.ARG_STAR2],
907903
[*prefix.arg_names, None, None],
908904
ret_type=ret_type,

0 commit comments

Comments
 (0)