8000 Polymorphic inference: support for parameter specifications and lambdas by ilevkivskyi · Pull Request #15837 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Polymorphic inference: support for parameter specifications and lambdas #15837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 15, 2023
Prev Previous commit
Next Next commit
Fix urllib crash; some assert tweak
  • Loading branch information
Ivan Levkivskyi committed Aug 9, 2023
commit 582a4deafd0a4c64ac6a53eb648b9f73502fc28b
10 changes: 7 additions & 3 deletions mypy/applytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
AnyType,
CallableType,
Instance,
Parameters,
ParamSpecType,
PartialType,
TupleType,
Expand Down Expand Up @@ -109,8 +108,13 @@ def apply_generic_arguments(
if param_spec is not None:
nt = id_to_type.get(param_spec.id)
if nt is not None:
if isinstance(nt, Parameters):
callable = callable.expand_param_spec(nt)
# ParamSpec expansion is special-cased, so we need to always expand callable
# as a whole, not expanding arguments individually.
callable = expand_type(callable, id_to_type)
assert isinstance(callable, CallableType)
return callable.copy_modified(
variables=[tv for tv in tvars if tv.id not in id_to_type]
)

# Apply arguments to argument types.
var_arg = callable.var_arg()
Expand Down
2 changes: 1 addition & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,7 @@ def __init__(
self.arg_kinds = arg_kinds
self.arg_names = list(arg_names)
assert len(arg_types) == len(arg_kinds) == len(arg_names)
assert not any(isinstance(t, (Parameters, ParamSpecType)) for t in arg_types)
assert not any(isinstance(t, Parameters) for t in arg_types)
self.min_args = arg_kinds.count(ARG_POS)
self.is_ellipsis_args = is_ellipsis_args
self.variables = variables or []
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -1555,3 +1555,16 @@ def test(x: U) -> U: ...
reveal_type(dec) # N: Revealed type is "def [P, T] (f: def (*P.args, **P.kwargs) -> T`-2) -> def (*P.args, **P.kwargs) -> builtins.list[T`-2]"
reveal_type(dec(test)) # N: Revealed type is "def [T] (x: T`2) -> builtins.list[T`2]"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add a bit kinda like this?:

class A: ...
TA = TypeVar("T", bound=A)

def test_with_bound(x: TA) -> TA: ...
reveal_type(dec(test_with_bound))
dec(test_with_bound)(0)  # should error
dec(test_with_bound)(A())  # should be AOK

? I'm a bit concerned about the replacing of type variables here, though I do remember seeing something in an earlier PR about updating bounds so that's probably already handled.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, upper bounds should be ~decently handled. I added the test.

[builtins fixtures/paramspec.pyi]

[case testParamSpecNestedApplyNoCrash]
from typing import Callable, TypeVar
from typing_extensions import ParamSpec

P = ParamSpec("P")
T = TypeVar("T")

def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ...
def test() -> None: ...
# TODO: avoid this error, although it may be non-trivial.
apply(apply, test) # E: Argument 2 to "apply" has incompatible type "Callable[[], None]"; expected "Callable[P, T]"
[builtins fixtures/paramspec.pyi]
0