Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2007165
[WIP] Include param_spec in callable types and support reveal_Type
JukkaL Nov 18, 2021
da6d041
[WIP] Simple type inference involving ParamSpec
JukkaL Nov 18, 2021
2506647
[WIP] Work towards using ParamSpec in classes
JukkaL Nov 18, 2021
56971f0
[WIP] Support defining decorators
JukkaL Nov 18, 2021
774a558
Various fixes
JukkaL Nov 19, 2021
cc8fc46
Fixes to mypy daemon (not tested yet)
JukkaL Nov 19, 2021
5b8a1c6
Various updates/fixes to ParamSpecType
JukkaL Nov 19, 2021
bb928ce
Fix to type semantic analysis
JukkaL Nov 19, 2021
f3a2596
Add missing get_proper_type call
JukkaL Nov 19, 2021
ef52db7
Fixes to self check
JukkaL Nov 19, 2021
c76ce08
Fix type check of mypy.constraints
JukkaL Nov 19, 2021
8a69923
Add missing get_proper_type() calls
JukkaL Nov 19, 2021
76ffb14
Move some param spec logic to CallableType
JukkaL Nov 19, 2021
be6f8e8
Fix applying types with ParamSpec
JukkaL Nov 19, 2021
c1686b0
Switch to simpler ParamSpec implementation + cleanup
JukkaL Nov 19, 2021
a3cb0ad
Fix inference against Any
JukkaL Nov 19, 2021
fd124c5
Update test cases
JukkaL Nov 19, 2021
22afc1d
Don't wrap ParamSpec *args and **kwargs types in tuple[...] or dict[...]
JukkaL Nov 19, 2021
044ddfd
Implement basic type checking for ParamSpec types
JukkaL Nov 19, 2021
6d25da4
Use Callable[P, T] in error messages
JukkaL Nov 22, 2021
1047f85
Support subtype checks
JukkaL Nov 22, 2021
a9b410e
Implement ParamSpec join
JukkaL Nov 22, 2021
f621fb7
Fix type erasure
JukkaL Nov 22, 2021
080049e
Add test case
JukkaL Nov 22, 2021
504267c
Disallow ParamSpec type in bad location
JukkaL Nov 22, 2021
2d02b62
Fix type check and lint
JukkaL Nov 22, 2021
dd28b93
Minor tweaks
JukkaL Nov 22, 2021
01ae7d5
Fix TODO in string conversion
JukkaL Nov 22, 2021
b6b09f1
Update docstrings
JukkaL Nov 22, 2021
1af097f
Respond to feedback
JukkaL Nov 23, 2021
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
Prev Previous commit
Next Next commit
Don't wrap ParamSpec *args and **kwargs types in tuple[...] or dict[...]
  • Loading branch information
JukkaL committed Nov 22, 2021
commit 22afc1d2bbd2a33bfc995c21dd473a6bf2d259f5
15 changes: 9 additions & 6 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType,
is_named_instance, union_items, TypeQuery, LiteralType,
is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType,
get_proper_types, is_literal_type, TypeAliasType, TypeGuardedType)
get_proper_types, is_literal_type, TypeAliasType, TypeGuardedType, ParamSpecType
)
from mypy.sametypes import is_same_type
from mypy.messages import (
MessageBuilder, make_inferred_type_note, append_invariance_notes, pretty_seq,
Expand Down Expand Up @@ -977,12 +978,14 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
self.fail(message_registry.FUNCTION_PARAMETER_CANNOT_BE_COVARIANT, ctx)
if typ.arg_kinds[i] == nodes.ARG_STAR:
# builtins.tuple[T] is typing.Tuple[T, ...]
arg_type = self.named_generic_type('builtins.tuple',
[arg_type])
if not isinstance(arg_type, ParamSpecType):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should update the proper_plugin to not require this. ParamSpecType can never by a target of a type alias. (Note TypeVarType is already excluded).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good idea! Done. Also removed various now-redundant get_proper_type calls.

arg_type = self.named_generic_type('builtins.tuple',
[arg_type])
elif typ.arg_kinds[i] == nodes.ARG_STAR2:
arg_type = self.named_generic_type('builtins.dict',
[self.str_type(),
arg_type])
if not isinstance(arg_type, ParamSpecType):
arg_type = self.named_generic_type('builtins.dict',
[self.str_type(),
arg_type])
item.arguments[i].variable.type = arg_type

# Type check initialization expressions.
Expand Down
9 changes: 6 additions & 3 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
Type, AnyType, CallableType, Overloaded, NoneType, TypeVarType,
TupleType, TypedDictType, Instance, ErasedType, UnionType,
PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, LiteralType, LiteralValue,
is_named_instance, FunctionLike, ParamSpecType,
is_named_instance, FunctionLike, ParamSpecType, ParamSpecFlavor,
StarType, is_optional, remove_optional, is_generic_instance, get_proper_type, ProperType,
get_proper_types, flatten_nested_unions
)
Expand Down Expand Up @@ -3999,15 +3999,18 @@ def is_valid_var_arg(self, typ: Type) -> bool:
return (isinstance(typ, TupleType) or
is_subtype(typ, self.chk.named_generic_type('typing.Iterable',
[AnyType(TypeOfAny.special_form)])) or
isinstance(typ, AnyType))
isinstance(typ, AnyType) or
(isinstance(typ, ParamSpecType) and typ.flavor == ParamSpecFlavor.ARGS))

def is_valid_keyword_var_arg(self, typ: Type) -> bool:
"""Is a type valid as a **kwargs argument?"""
ret = (
is_subtype(typ, self.chk.named_generic_type('typing.Mapping',
[self.named_type('builtins.str'), AnyType(TypeOfAny.special_form)])) or
is_subtype(typ, self.chk.named_generic_type('typing.Mapping',
[UninhabitedType(), UninhabitedType()])))
[UninhabitedType(), UninhabitedType()])) or
(isinstance(typ, ParamSpecType) and typ.flavor == ParamSpecFlavor.KWARGS)
)
if self.chk.options.python_version[0] < 3:
ret = ret or is_subtype(typ, self.chk.named_generic_type('typing.Mapping',
[self.named_type('builtins.unicode'), AnyType(TypeOfAny.special_form)]))
Expand Down
9 changes: 7 additions & 2 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2176,10 +2176,15 @@ def visit_type_var(self, t: TypeVarType) -> str:
def visit_param_spec(self, t: ParamSpecType) -> str:
if t.name is None:
# Anonymous type variable type (only numeric id).
s = '`{}'.format(t.id)
s = f'`{t.id}'
else:
# Named type variable type.
s = '{}`{}'.format(t.name, t.id)
suffix = ''
if t.flavor == ParamSpecFlavor.ARGS:
suffix = '.args'
elif t.flavor == ParamSpecFlavor.KWARGS:
suffix = '.kwargs'
s = f'{t.name}{suffix}`{t.id}'
return s

def visit_callable_type(self, t: CallableType) -> str:
Expand Down
14 changes: 14 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,17 @@ def g(x: int, y: str = '') -> int: ...

reveal_type(g) # N: Revealed type is "def (x: builtins.int, y: builtins.str =) -> builtins.list[builtins.int*]"
[builtins fixtures/dict.pyi]

[case testParamSpecArgsAndKwargsTypes]
from typing import Callable, TypeVar, Generic
from typing_extensions import ParamSpec

P = ParamSpec('P')

class C(Generic[P]):
def __init__(self, x: Callable[P, None]) -> None: ...

def m(self, *args: P.args, **kwargs: P.kwargs) -> None:
reveal_type(args) # N: Revealed type is "P.args`1"
reveal_type(kwargs) # N: Revealed type is "P.kwargs`1"
[builtins fixtures/dict.pyi]