Support `expand_type` with `ParamSpec.{args,kwargs}`
Fixes #19839.
Looks like it was relatively easy to do the right way, let me try! When splitting a callable/parameters into args and kwargs, we have the following options:
- posonly - only goes to
*args, required unless has a default. If we encounter such required arg, all previously collected optional args become required (this only happens due to faulty TVT expansion somewhere; probably I should look into that too) - kwonly - only goes to
**kwargs, required unless has a default - pos-or-kw - goes to both
- vararg - only goes to
*argsas anUnpack(possibly normalized by tuple constructor) - kwargs - only goes to
**kwargsand is only used if there are no kwargs with known names, because PEP 728 is not yet implemented, so we have to choose betweendictandTypedDict. (thoughts? Maybe it is better to preferdictwithunion(kwarg, *kwargs.values())as value type? Either way I do not consider this question important as PEP728 will be eventually implemented, and we'll haveextra_itemsfor ourTypedDicts)
Applying these steps to every argument in order, we collect required and optional args and kwargs candidates. Now, the type of **kwargs is a TypedDict if we know any keys, dict[str, KwargType] if we only have something like **kw: str, and dict[str, Never] if no kwargs were found.
The type of *args is union of all prefixes of optional_args concatenated with required_args: all required args must be there, and optional args can only be passed in order. Since it is uncommon to have a function with more than 10-20 args, I think this union is a reasonable solution.
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅
@sterliakov Btw I just noticed that many of the unions in revealed types can be simplified by mypy.binder.collapse_variadic_union(), is it just a coincidence? If it is not a coincidence, then maybe you can re-use that helper here? (This will probably require moving it to typevartuples.py)
Hm, looking at this again this is likely just a coincidence, because of
def f5(x: int, y: int = 0) -> int:
return 0
def f6(x: int, *args: int) -> int:
return 0
if the types would be different (not both ints), then there would be no compact representation.
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅