Skip to content

Commit

Permalink
Use tuple[object, ...] and dict[str, object] as upper bounds for Para…
Browse files Browse the repository at this point in the history
…mSpec.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.
  • Loading branch information
AlexWaygood committed Apr 29, 2022
1 parent d48d548 commit c6cf7cd
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 47 deletions.
2 changes: 1 addition & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def _analyze_member_access(name: str,
return analyze_typeddict_access(name, typ, mx, override_info)
elif isinstance(typ, NoneType):
return analyze_none_member_access(name, typ, mx)
elif isinstance(typ, TypeVarType):
elif isinstance(typ, TypeVarLikeType):
return _analyze_member_access(name, typ.upper_bound, mx, override_info)
elif isinstance(typ, DeletedType):
mx.msg.deleted_as_rvalue(typ, mx.context)
Expand Down
50 changes: 47 additions & 3 deletions mypy/semanal_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

from abc import abstractmethod

from typing import Optional, List, Callable
from typing_extensions import Final
from typing import Optional, List, Callable, Union
from typing_extensions import Final, Protocol
from mypy_extensions import trait

from mypy.nodes import (
Context, SymbolTableNode, FuncDef, Node, TypeInfo, Expression,
SymbolNode, SymbolTable
)
from mypy.types import (
Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type
Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type,
ParamSpecType, ParamSpecFlavor, Parameters, TypeVarId
)
from mypy.tvar_scope import TypeVarLikeScope
from mypy.errorcodes import ErrorCode
Expand Down Expand Up @@ -212,3 +213,46 @@ def calculate_tuple_fallback(typ: TupleType) -> None:
fallback = typ.partial_fallback
assert fallback.type.fullname == 'builtins.tuple'
fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:]


class _NamedTypeCallback(Protocol):
def __call__(
self, fully_qualified_name: str, args: Optional[List[Type]] = None
) -> Instance: ...


def paramspec_args(
name: str, fullname: str, id: Union[TypeVarId, int], *,
named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1,
prefix: Optional[Parameters] = None
) -> ParamSpecType:
return ParamSpecType(
name,
fullname,
id,
flavor=ParamSpecFlavor.ARGS,
upper_bound=named_type_func('builtins.tuple', [named_type_func('builtins.object')]),
line=line,
column=column,
prefix=prefix
)


def paramspec_kwargs(
name: str, fullname: str, id: Union[TypeVarId, int], *,
named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1,
prefix: Optional[Parameters] = None
) -> ParamSpecType:
return ParamSpecType(
name,
fullname,
id,
flavor=ParamSpecFlavor.KWARGS,
upper_bound=named_type_func(
'builtins.dict',
[named_type_func('builtins.str'), named_type_func('builtins.object')]
),
line=line,
column=column,
prefix=prefix
)
30 changes: 13 additions & 17 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from mypy.tvar_scope import TypeVarLikeScope
from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError
from mypy.plugin import Plugin, TypeAnalyzerPluginInterface, AnalyzeTypeContext
from mypy.semanal_shared import SemanticAnalyzerCoreInterface
from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs
from mypy.errorcodes import ErrorCode
from mypy import nodes, message_registry, errorcodes as codes

Expand Down Expand Up @@ -711,13 +711,13 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type:
tvar_def = self.tvar_scope.get_binding(sym)
if isinstance(tvar_def, ParamSpecType):
if kind == ARG_STAR:
flavor = ParamSpecFlavor.ARGS
make_paramspec = paramspec_args
elif kind == ARG_STAR2:
flavor = ParamSpecFlavor.KWARGS
make_paramspec = paramspec_kwargs
else:
assert False, kind
return ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, flavor,
upper_bound=self.named_type('builtins.object'),
return make_paramspec(tvar_def.name, tvar_def.fullname, tvar_def.id,
named_type_func=self.named_type,
line=t.line, column=t.column)
return self.anal_type(t, nested=nested)

Expand Down Expand Up @@ -855,13 +855,11 @@ def analyze_callable_args_for_paramspec(
if not isinstance(tvar_def, ParamSpecType):
return None

# TODO: Use tuple[...] or Mapping[..] instead?
obj = self.named_type('builtins.object')
return CallableType(
[ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS,
upper_bound=obj),
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS,
upper_bound=obj)],
[paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id,
named_type_func=self.named_type),
paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id,
named_type_func=self.named_type)],
[nodes.ARG_STAR, nodes.ARG_STAR2],
[None, None],
ret_type=ret_type,
Expand Down Expand Up @@ -891,18 +889,16 @@ def analyze_callable_args_for_concatenate(
if not isinstance(tvar_def, ParamSpecType):
return None

# TODO: Use tuple[...] or Mapping[..] instead?
obj = self.named_type('builtins.object')
# ick, CallableType should take ParamSpecType
prefix = tvar_def.prefix
# we don't set the prefix here as generic arguments will get updated at some point
# in the future. CallableType.param_spec() accounts for this.
return CallableType(
[*prefix.arg_types,
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS,
upper_bound=obj),
ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS,
upper_bound=obj)],
paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id,
named_type_func=self.named_type),
paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id,
named_type_func=self.named_type)],
[*prefix.arg_kinds, nodes.ARG_STAR, nodes.ARG_STAR2],
[*prefix.arg_names, None, None],
ret_type=ret_type,
Expand Down
Loading

0 comments on commit c6cf7cd

Please sign in to comment.