@@ -15,8 +15,9 @@ def foo1(x: Callable[P, int]) -> Callable[P, str]: ...
1515def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \
1616 # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]'
1717
18- # TODO(PEP612): uncomment once we have support for Concatenate
19- # def foo3(x: Concatenate[int, P]) -> int: ... $ E: Invalid location for Concatenate
18+ # TODO: Better error message
19+ def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for ParamSpec "P" \
20+ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]'
2021
2122def foo4(x: List[P]) -> None: ... # E: Invalid location for ParamSpec "P" \
2223 # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]'
@@ -455,5 +456,82 @@ reveal_type(kb(n)) # N: Revealed type is "__main__.Z[[builtins.str]]" \
455456# TODO(PEP612): fancy "aesthetic" syntax defined in PEP
456457# n2: Z[bytes]
457458#
458- # reveal_type(kb(n2)) # N: Revealed type is "__main__.Z[[builtins.str]]"
459+ # reveal_type(kb(n2)) $ N: Revealed type is "__main__.Z[[builtins.str]]"
459460[builtins fixtures/tuple.pyi]
461+
462+ [case testParamSpecConcatenateFromPep]
463+ from typing_extensions import ParamSpec, Concatenate
464+ from typing import Callable, TypeVar, Generic
465+
466+ P = ParamSpec("P")
467+ R = TypeVar("R")
468+
469+ # CASE 1
470+ class Request:
471+ ...
472+
473+ def with_request(f: Callable[Concatenate[Request, P], R]) -> Callable[P, R]:
474+ def inner(*args: P.args, **kwargs: P.kwargs) -> R:
475+ return f(Request(), *args, **kwargs)
476+ return inner
477+
478+ @with_request
479+ def takes_int_str(request: Request, x: int, y: str) -> int:
480+ # use request
481+ return x + 7
482+
483+ reveal_type(takes_int_str) # N: Revealed type is "def (x: builtins.int, y: builtins.str) -> builtins.int*"
484+
485+ takes_int_str(1, "A") # Accepted
486+ takes_int_str("B", 2) # E: Argument 1 to "takes_int_str" has incompatible type "str"; expected "int" \
487+ # E: Argument 2 to "takes_int_str" has incompatible type "int"; expected "str"
488+
489+ # CASE 2
490+ T = TypeVar("T")
491+ P_2 = ParamSpec("P_2")
492+
493+ class X(Generic[T, P]):
494+ f: Callable[P, int]
495+ x: T
496+
497+ def f1(x: X[int, P_2]) -> str: ... # Accepted
498+ def f2(x: X[int, Concatenate[int, P_2]]) -> str: ... # Accepted
499+ def f3(x: X[int, [int, bool]]) -> str: ... # Accepted
500+ # Is ellipsis allowed by PEP? This shows up:
501+ # def f4(x: X[int, ...]) -> str: ... # Accepted
502+ # TODO: this is not rejected:
503+ # def f5(x: X[int, int]) -> str: ... # Rejected
504+
505+ # CASE 3
506+ def bar(x: int, *args: bool) -> int: ...
507+ def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ...
508+
509+ reveal_type(add(bar)) # N: Revealed type is "def (builtins.str, x: builtins.int, *args: builtins.bool) -> builtins.bool"
510+
511+ def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ...
512+
513+ reveal_type(remove(bar)) # N: Revealed type is "def (*args: builtins.bool) -> builtins.bool"
514+
515+ def transform(
516+ x: Callable[Concatenate[int, P], int]
517+ ) -> Callable[Concatenate[str, P], bool]: ...
518+
519+ # In the PEP, "__a" appears. What is that? Autogenerated names? To what spec?
520+ reveal_type(transform(bar)) # N: Revealed type is "def (builtins.str, *args: builtins.bool) -> builtins.bool"
521+
522+ # CASE 4
523+ def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: ...
524+
525+ @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]"
526+ def one(x: str) -> int: ...
527+
528+ @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[int], int]"
529+ def two(*, x: int) -> int: ...
530+
531+ @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[KwArg(int)], int]"; expected "Callable[[int], int]"
532+ def three(**kwargs: int) -> int: ...
533+
534+ @expects_int_first # Accepted
535+ def four(*args: int) -> int: ...
536+ [builtins fixtures/tuple.pyi]
537+ [builtins fixtures/dict.pyi]
0 commit comments