| [case testBasicParamSpec] | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| [builtins fixtures/tuple.pyi] | |
| [case testInvalidParamSpecDefinitions] | |
| from typing import ParamSpec | |
| P1 = ParamSpec("P1", covariant=True) # E: The variance and bound arguments to ParamSpec do not have defined semantics yet | |
| P2 = ParamSpec("P2", contravariant=True) # E: The variance and bound arguments to ParamSpec do not have defined semantics yet | |
| P3 = ParamSpec("P3", bound=int) # E: The variance and bound arguments to ParamSpec do not have defined semantics yet | |
| P4 = ParamSpec("P4", int, str) # E: Too many positional arguments for "ParamSpec" | |
| P5 = ParamSpec("P5", covariant=True, bound=int) # E: The variance and bound arguments to ParamSpec do not have defined semantics yet | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecLocations] | |
| from typing import Callable, List | |
| from typing_extensions import ParamSpec, Concatenate | |
| P = ParamSpec('P') | |
| x: P # E: ParamSpec "P" is unbound | |
| def foo1(x: Callable[P, int]) -> Callable[P, str]: ... | |
| def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \ | |
| # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' | |
| def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate \ | |
| # N: You can use Concatenate as the first argument to Callable | |
| def foo4(x: List[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ | |
| # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' | |
| def foo5(x: Callable[[int, str], P]) -> None: ... # E: Invalid location for ParamSpec "P" \ | |
| # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' | |
| def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpec "P" \ | |
| # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecContextManagerLike] | |
| from typing import Callable, List, Iterator, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| T = TypeVar('T') | |
| def tmpcontextmanagerlike(x: Callable[P, Iterator[T]]) -> Callable[P, List[T]]: ... | |
| @tmpcontextmanagerlike | |
| def whatever(x: int) -> Iterator[int]: | |
| yield x | |
| reveal_type(whatever) # N: Revealed type is "def (x: builtins.int) -> builtins.list[builtins.int]" | |
| reveal_type(whatever(217)) # N: Revealed type is "builtins.list[builtins.int]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testInvalidParamSpecType] | |
| # flags: --python-version 3.10 | |
| from typing import ParamSpec | |
| P = ParamSpec("P") | |
| class MyFunction(P): # E: Invalid base class "P" | |
| ... | |
| [case testParamSpecRevealType] | |
| from typing import Callable | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| def f(x: Callable[P, int]) -> None: ... | |
| reveal_type(f) # N: Revealed type is "def [P] (x: def (*P.args, **P.kwargs) -> builtins.int)" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecSimpleFunction] | |
| from typing import Callable, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| def changes_return_type_to_str(x: Callable[P, int]) -> Callable[P, str]: ... | |
| def returns_int(a: str, b: bool) -> int: ... | |
| reveal_type(changes_return_type_to_str(returns_int)) # N: Revealed type is "def (a: builtins.str, b: builtins.bool) -> builtins.str" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecSimpleClass] | |
| 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) -> int: | |
| return 1 | |
| def f(x: int, y: str) -> None: ... | |
| reveal_type(C(f)) # N: Revealed type is "__main__.C[[x: builtins.int, y: builtins.str]]" | |
| reveal_type(C(f).m) # N: Revealed type is "def (x: builtins.int, y: builtins.str) -> builtins.int" | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecClassWithPrefixArgument] | |
| 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, a: str, *args: P.args, **kwargs: P.kwargs) -> int: | |
| return 1 | |
| def f(x: int, y: str) -> None: ... | |
| reveal_type(C(f).m) # N: Revealed type is "def (a: builtins.str, x: builtins.int, y: builtins.str) -> builtins.int" | |
| reveal_type(C(f).m('', 1, '')) # N: Revealed type is "builtins.int" | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecDecorator] | |
| from typing import Callable, TypeVar, Generic | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| R = TypeVar('R') | |
| class W(Generic[P, R]): | |
| f: Callable[P, R] | |
| x: int | |
| def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: | |
| reveal_type(self.f(*args, **kwargs)) # N: Revealed type is "R`2" | |
| return self.f(*args, **kwargs) | |
| def dec() -> Callable[[Callable[P, R]], W[P, R]]: | |
| pass | |
| @dec() | |
| def f(a: int, b: str) -> None: ... | |
| reveal_type(f) # N: Revealed type is "__main__.W[[a: builtins.int, b: builtins.str], None]" | |
| reveal_type(f(1, '')) # N: Revealed type is "None" | |
| reveal_type(f.x) # N: Revealed type is "builtins.int" | |
| ## TODO: How should this work? | |
| # | |
| # class C: | |
| # @dec() | |
| # def m(self, x: int) -> str: ... | |
| # | |
| # reveal_type(C().m(x=1)) | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecFunction] | |
| from typing import Callable, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| R = TypeVar('R') | |
| def f(x: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: | |
| return x(*args, **kwargs) | |
| def g(x: int, y: str) -> None: ... | |
| reveal_type(f(g, 1, y='x')) # N: Revealed type is "None" | |
| f(g, 'x', y='x') # E: Argument 2 to "f" has incompatible type "str"; expected "int" | |
| f(g, 1, y=1) # E: Argument "y" to "f" has incompatible type "int"; expected "str" | |
| f(g) # E: Missing positional arguments "x", "y" in call to "f" | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecSpecialCase] | |
| from typing import Callable, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| T = TypeVar('T') | |
| def register(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[P, T]: ... | |
| def f(x: int, y: str, z: int, a: str) -> None: ... | |
| x = register(f, 1, '', 1, '') | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecInferredFromAny] | |
| from typing import Callable, Any | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| def f(x: Callable[P, int]) -> Callable[P, str]: ... | |
| g: Any | |
| reveal_type(f(g)) # N: Revealed type is "def (*Any, **Any) -> builtins.str" | |
| f(g)(1, 3, x=1, y=2) | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecDecoratorImplementation] | |
| from typing import Callable, Any, TypeVar, List | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| T = TypeVar('T') | |
| def dec(f: Callable[P, T]) -> Callable[P, List[T]]: | |
| def wrapper(*args: P.args, **kwargs: P.kwargs) -> List[T]: | |
| return [f(*args, **kwargs)] | |
| return wrapper | |
| @dec | |
| 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] | |
| [case testParamSpecSubtypeChecking1] | |
| from typing import Callable, TypeVar, Generic, Any | |
| 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: | |
| args = args | |
| kwargs = kwargs | |
| o: object | |
| o = args | |
| o = kwargs | |
| o2: object | |
| args = o2 # E: Incompatible types in assignment (expression has type "object", variable has type "P.args") | |
| kwargs = o2 # E: Incompatible types in assignment (expression has type "object", variable has type "P.kwargs") | |
| a: Any | |
| a = args | |
| a = kwargs | |
| args = kwargs # E: Incompatible types in assignment (expression has type "P.kwargs", variable has type "P.args") | |
| kwargs = args # E: Incompatible types in assignment (expression has type "P.args", variable has type "P.kwargs") | |
| args = a | |
| kwargs = a | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecSubtypeChecking2] | |
| from typing import Callable, Generic | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| P2 = ParamSpec('P2') | |
| class C(Generic[P]): | |
| pass | |
| def f(c1: C[P], c2: C[P2]) -> None: | |
| c1 = c1 | |
| c2 = c2 | |
| c1 = c2 # E: Incompatible types in assignment (expression has type "C[P2]", variable has type "C[P]") | |
| c2 = c1 # E: Incompatible types in assignment (expression has type "C[P]", variable has type "C[P2]") | |
| def g(f: Callable[P, None], g: Callable[P2, None]) -> None: | |
| f = f | |
| g = g | |
| f = g # E: Incompatible types in assignment (expression has type "Callable[P2, None]", variable has type "Callable[P, None]") | |
| g = f # E: Incompatible types in assignment (expression has type "Callable[P, None]", variable has type "Callable[P2, None]") | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecJoin] | |
| from typing import Callable, Generic, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| P2 = ParamSpec('P2') | |
| P3 = ParamSpec('P3') | |
| T = TypeVar('T') | |
| def join(x: T, y: T) -> T: ... | |
| class C(Generic[P, P2]): | |
| def m(self, f: Callable[P, None], g: Callable[P2, None]) -> None: | |
| reveal_type(join(f, f)) # N: Revealed type is "def (*P.args, **P.kwargs)" | |
| reveal_type(join(f, g)) # N: Revealed type is "builtins.function" | |
| def m2(self, *args: P.args, **kwargs: P.kwargs) -> None: | |
| reveal_type(join(args, args)) # N: Revealed type is "P.args`1" | |
| reveal_type(join(kwargs, kwargs)) # N: Revealed type is "P.kwargs`1" | |
| reveal_type(join(args, kwargs)) # N: Revealed type is "builtins.object" | |
| def f(*args2: P2.args, **kwargs2: P2.kwargs) -> None: | |
| reveal_type(join(args, args2)) # N: Revealed type is "builtins.object" | |
| reveal_type(join(kwargs, kwargs2)) # N: Revealed type is "builtins.object" | |
| def m3(self, c: C[P, P3]) -> None: | |
| reveal_type(join(c, c)) # N: Revealed type is "__main__.C[P`1, P3`-1]" | |
| reveal_type(join(self, c)) # N: Revealed type is "builtins.object" | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecClassWithAny] | |
| from typing import Callable, Generic, Any | |
| 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) -> int: | |
| return 1 | |
| c: C[Any] | |
| reveal_type(c) # N: Revealed type is "__main__.C[Any]" | |
| reveal_type(c.m) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> builtins.int" | |
| c.m(4, 6, y='x') | |
| c = c | |
| def f() -> None: pass | |
| c2 = C(f) | |
| c2 = c | |
| c3 = C(f) | |
| c = c3 | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecInferredFromLambda] | |
| from typing import Callable, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| T = TypeVar('T') | |
| # Similar to atexit.register | |
| def register(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[P, T]: ... # N: "register" defined here | |
| def f(x: int) -> None: pass | |
| reveal_type(register(lambda: f(1))) # N: Revealed type is "def ()" | |
| reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (x: Any)" | |
| register(lambda x: f(x)) # E: Missing positional argument "x" in call to "register" | |
| register(lambda x: f(x), y=1) # E: Unexpected keyword argument "y" for "register" | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecInvalidCalls] | |
| from typing import Callable, Generic | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| P2 = ParamSpec('P2') | |
| class C(Generic[P, P2]): | |
| def m1(self, *args: P.args, **kwargs: P.kwargs) -> None: | |
| self.m1(*args, **kwargs) | |
| self.m2(*args, **kwargs) # E: Argument 1 to "m2" of "C" has incompatible type "*P.args"; expected "P2.args" \ | |
| # E: Argument 2 to "m2" of "C" has incompatible type "**P.kwargs"; expected "P2.kwargs" | |
| self.m1(*kwargs, **args) # E: Argument 1 to "m1" of "C" has incompatible type "*P.kwargs"; expected "P.args" \ | |
| # E: Argument 2 to "m1" of "C" has incompatible type "**P.args"; expected "P.kwargs" | |
| self.m3(*args, **kwargs) # E: Argument 1 to "m3" of "C" has incompatible type "*P.args"; expected "int" \ | |
| # E: Argument 2 to "m3" of "C" has incompatible type "**P.kwargs"; expected "int" | |
| self.m4(*args, **kwargs) # E: Argument 1 to "m4" of "C" has incompatible type "*P.args"; expected "int" \ | |
| # E: Argument 2 to "m4" of "C" has incompatible type "**P.kwargs"; expected "int" | |
| self.m1(*args, **args) # E: Argument 2 to "m1" of "C" has incompatible type "**P.args"; expected "P.kwargs" | |
| self.m1(*kwargs, **kwargs) # E: Argument 1 to "m1" of "C" has incompatible type "*P.kwargs"; expected "P.args" | |
| def m2(self, *args: P2.args, **kwargs: P2.kwargs) -> None: | |
| pass | |
| def m3(self, *args: int, **kwargs: int) -> None: | |
| pass | |
| def m4(self, x: int) -> None: | |
| pass | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecOverUnannotatedDecorator] | |
| from typing import Callable, Iterator, TypeVar, ContextManager, Any | |
| from typing_extensions import ParamSpec | |
| from nonexistent import deco2 # type: ignore | |
| T = TypeVar("T") | |
| P = ParamSpec("P") | |
| T_co = TypeVar("T_co", covariant=True) | |
| class CM(ContextManager[T_co]): | |
| def __call__(self, func: T) -> T: ... | |
| def deco1( | |
| func: Callable[P, Iterator[T]]) -> Callable[P, CM[T]]: ... | |
| @deco1 | |
| @deco2 | |
| def f(): | |
| pass | |
| reveal_type(f) # N: Revealed type is "def (*Any, **Any) -> __main__.CM[Any]" | |
| with f() as x: | |
| pass | |
| [builtins fixtures/dict.pyi] | |
| [typing fixtures/typing-full.pyi] | |
| [case testParamSpecLiterals] | |
| from typing_extensions import ParamSpec, TypeAlias | |
| from typing import Generic, TypeVar | |
| P = ParamSpec("P") | |
| T = TypeVar("T") | |
| class Z(Generic[P]): ... | |
| # literals can be applied | |
| n: Z[[int]] | |
| nt1 = Z[[int]] | |
| nt2: TypeAlias = Z[[int]] | |
| unt1: nt1 | |
| unt2: nt2 | |
| # literals actually keep types | |
| reveal_type(n) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| reveal_type(unt1) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| reveal_type(unt2) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| # passing into a function keeps the type | |
| def fT(a: T) -> T: ... | |
| def fP(a: Z[P]) -> Z[P]: ... | |
| reveal_type(fT(n)) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| reveal_type(fP(n)) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| # literals can be in function args and return type | |
| def k(a: Z[[int]]) -> Z[[str]]: ... | |
| # functions work | |
| reveal_type(k(n)) # N: Revealed type is "__main__.Z[[builtins.str]]" | |
| # literals can be matched in arguments | |
| def kb(a: Z[[bytes]]) -> Z[[str]]: ... | |
| reveal_type(kb(n)) # N: Revealed type is "__main__.Z[[builtins.str]]" \ | |
| # E: Argument 1 to "kb" has incompatible type "Z[[int]]"; expected "Z[[bytes]]" | |
| n2: Z[bytes] | |
| reveal_type(kb(n2)) # N: Revealed type is "__main__.Z[[builtins.str]]" | |
| [builtins fixtures/tuple.pyi] | |
| [case testParamSpecConcatenateFromPep] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, TypeVar, Generic | |
| P = ParamSpec("P") | |
| R = TypeVar("R") | |
| # CASE 1 | |
| class Request: | |
| ... | |
| def with_request(f: Callable[Concatenate[Request, P], R]) -> Callable[P, R]: | |
| def inner(*args: P.args, **kwargs: P.kwargs) -> R: | |
| return f(Request(), *args, **kwargs) | |
| return inner | |
| @with_request | |
| def takes_int_str(request: Request, x: int, y: str) -> int: | |
| # use request | |
| return x + 7 | |
| reveal_type(takes_int_str) # N: Revealed type is "def (x: builtins.int, y: builtins.str) -> builtins.int" | |
| takes_int_str(1, "A") # Accepted | |
| takes_int_str("B", 2) # E: Argument 1 to "takes_int_str" has incompatible type "str"; expected "int" \ | |
| # E: Argument 2 to "takes_int_str" has incompatible type "int"; expected "str" | |
| # CASE 2 | |
| T = TypeVar("T") | |
| P_2 = ParamSpec("P_2") | |
| class X(Generic[T, P]): | |
| f: Callable[P, int] | |
| x: T | |
| def f1(x: X[int, P_2]) -> str: ... # Accepted | |
| def f2(x: X[int, Concatenate[int, P_2]]) -> str: ... # Accepted | |
| def f3(x: X[int, [int, bool]]) -> str: ... # Accepted | |
| # ellipsis only show up here, but I can assume it works like Callable[..., R] | |
| def f4(x: X[int, ...]) -> str: ... # Accepted | |
| def f5(x: X[int, int]) -> str: ... # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" | |
| # CASE 3 | |
| def bar(x: int, *args: bool) -> int: ... | |
| def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... | |
| reveal_type(add(bar)) # N: Revealed type is "def (builtins.str, x: builtins.int, *args: builtins.bool) -> builtins.bool" | |
| def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... | |
| reveal_type(remove(bar)) # N: Revealed type is "def (*args: builtins.bool) -> builtins.bool" | |
| def transform( | |
| x: Callable[Concatenate[int, P], int] | |
| ) -> Callable[Concatenate[str, P], bool]: ... | |
| # In the PEP, "__a" appears. What is that? Autogenerated names? To what spec? | |
| reveal_type(transform(bar)) # N: Revealed type is "def (builtins.str, *args: builtins.bool) -> builtins.bool" | |
| # CASE 4 | |
| def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: ... | |
| @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" \ | |
| # N: This is likely because "one" has named arguments: "x". Consider marking them positional-only | |
| def one(x: str) -> int: ... | |
| @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[int, NamedArg(int, 'x')], int]" | |
| def two(*, x: int) -> int: ... | |
| @expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[KwArg(int)], int]"; expected "Callable[[int, KwArg(int)], int]" | |
| def three(**kwargs: int) -> int: ... | |
| @expects_int_first # Accepted | |
| def four(*args: int) -> int: ... | |
| [builtins fixtures/dict.pyi] | |
| [case testParamSpecTwiceSolving] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, TypeVar | |
| P = ParamSpec("P") | |
| R = TypeVar("R") | |
| def f(one: Callable[Concatenate[int, P], R], two: Callable[Concatenate[str, P], R]) -> Callable[P, R]: ... | |
| a: Callable[[int, bytes], str] | |
| b: Callable[[str, bytes], str] | |
| reveal_type(f(a, b)) # N: Revealed type is "def (builtins.bytes) -> builtins.str" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecConcatenateInReturn] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, Protocol | |
| P = ParamSpec("P") | |
| def f(i: Callable[Concatenate[int, P], str]) -> Callable[Concatenate[int, P], str]: ... | |
| n: Callable[[int, bytes], str] | |
| reveal_type(f(n)) # N: Revealed type is "def (builtins.int, builtins.bytes) -> builtins.str" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecConcatenateNamedArgs] | |
| # flags: --python-version 3.8 --extra-checks | |
| # this is one noticeable deviation from PEP but I believe it is for the better | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, TypeVar | |
| P = ParamSpec("P") | |
| R = TypeVar("R") | |
| def f1(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: | |
| def result(x: int, /, *args: P.args, **kwargs: P.kwargs) -> R: ... | |
| return result # Accepted | |
| def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: | |
| def result(x: int, *args: P.args, **kwargs: P.kwargs) -> R: ... | |
| return result # Rejected | |
| # reason for rejection: | |
| f2(lambda x: 42)(42, x=42) | |
| [builtins fixtures/paramspec.pyi] | |
| [out] | |
| main:10: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer | |
| [out version>=3.8] | |
| main:17: error: Incompatible return value type (got "Callable[[Arg(int, 'x'), **P], R]", expected "Callable[[int, **P], R]") | |
| main:17: note: This is likely because "result" has named arguments: "x". Consider marking them positional-only | |
| [case testNonStrictParamSpecConcatenateNamedArgs] | |
| # flags: --python-version 3.8 | |
| # this is one noticeable deviation from PEP but I believe it is for the better | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, TypeVar | |
| P = ParamSpec("P") | |
| R = TypeVar("R") | |
| def f1(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: | |
| def result(x: int, /, *args: P.args, **kwargs: P.kwargs) -> R: ... | |
| return result # Accepted | |
| def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: | |
| def result(x: int, *args: P.args, **kwargs: P.kwargs) -> R: ... | |
| return result # Rejected -> Accepted | |
| # reason for rejection: | |
| f2(lambda x: 42)(42, x=42) | |
| [builtins fixtures/paramspec.pyi] | |
| [out] | |
| main:11: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer | |
| [out version>=3.8] | |
| [case testParamSpecConcatenateWithTypeVar] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, TypeVar | |
| P = ParamSpec("P") | |
| R = TypeVar("R") | |
| S = TypeVar("S") | |
| def f(c: Callable[Concatenate[S, P], R]) -> Callable[Concatenate[S, P], R]: ... | |
| def a(n: int) -> None: ... | |
| n = f(a) | |
| reveal_type(n) # N: Revealed type is "def (builtins.int)" | |
| reveal_type(n(42)) # N: Revealed type is "None" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testCallablesAsParameters] | |
| # flags: --python-version 3.8 | |
| # credits to https://github.com/microsoft/pyright/issues/2705 | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Generic, Callable, Any | |
| P = ParamSpec("P") | |
| class Foo(Generic[P]): | |
| def __init__(self, func: Callable[P, Any]) -> None: ... | |
| def bar(baz: Foo[Concatenate[int, P]]) -> Foo[P]: ... | |
| def test(a: int, /, b: str) -> str: ... | |
| abc = Foo(test) | |
| reveal_type(abc) | |
| bar(abc) | |
| [builtins fixtures/paramspec.pyi] | |
| [out] | |
| main:13: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer | |
| [out version>=3.8] | |
| main:16: note: Revealed type is "__main__.Foo[[builtins.int, b: builtins.str]]" | |
| [case testSolveParamSpecWithSelfType] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Callable, Generic | |
| P = ParamSpec("P") | |
| class Foo(Generic[P]): | |
| def foo(self: 'Foo[P]', other: Callable[P, None]) -> None: ... | |
| n: Foo[[int]] | |
| def f(x: int) -> None: ... | |
| n.foo(f) | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecLiteralsTypeApplication] | |
| from typing_extensions import ParamSpec | |
| from typing import Generic, Callable | |
| P = ParamSpec("P") | |
| class Z(Generic[P]): | |
| def __init__(self, c: Callable[P, None]) -> None: | |
| ... | |
| # it allows valid functions | |
| reveal_type(Z[[int]](lambda x: None)) # N: Revealed type is "__main__.Z[[builtins.int]]" | |
| reveal_type(Z[[]](lambda: None)) # N: Revealed type is "__main__.Z[[]]" | |
| reveal_type(Z[bytes, str](lambda b, s: None)) # N: Revealed type is "__main__.Z[[builtins.bytes, builtins.str]]" | |
| # it disallows invalid functions | |
| def f1(n: str) -> None: ... | |
| def f2(b: bytes, i: int) -> None: ... | |
| Z[[int]](lambda one, two: None) # E: Cannot infer type of lambda \ | |
| # E: Argument 1 to "Z" has incompatible type "Callable[[Any, Any], None]"; expected "Callable[[int], None]" | |
| Z[[int]](f1) # E: Argument 1 to "Z" has incompatible type "Callable[[str], None]"; expected "Callable[[int], None]" | |
| Z[[]](lambda one: None) # E: Cannot infer type of lambda \ | |
| # E: Argument 1 to "Z" has incompatible type "Callable[[Any], None]"; expected "Callable[[], None]" | |
| Z[bytes, str](lambda one: None) # E: Cannot infer type of lambda \ | |
| # E: Argument 1 to "Z" has incompatible type "Callable[[Any], None]"; expected "Callable[[bytes, str], None]" | |
| Z[bytes, str](f2) # E: Argument 1 to "Z" has incompatible type "Callable[[bytes, int], None]"; expected "Callable[[bytes, str], None]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecLiteralEllipsis] | |
| from typing_extensions import ParamSpec | |
| from typing import Generic, Callable | |
| P = ParamSpec("P") | |
| class Z(Generic[P]): | |
| def __init__(self: 'Z[P]', c: Callable[P, None]) -> None: | |
| ... | |
| def f1() -> None: ... | |
| def f2(*args: int) -> None: ... | |
| def f3(a: int, *, b: bytes) -> None: ... | |
| def f4(b: bytes) -> None: ... | |
| argh: Callable[..., None] = f4 | |
| # check it works | |
| Z[...](f1) | |
| Z[...](f2) | |
| Z[...](f3) | |
| # check subtyping works | |
| n: Z[...] | |
| n = Z(f1) | |
| n = Z(f2) | |
| n = Z(f3) | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecApplyConcatenateTwice] | |
| from typing_extensions import ParamSpec, Concatenate | |
| from typing import Generic, Callable, Optional | |
| P = ParamSpec("P") | |
| class C(Generic[P]): | |
| # think PhantomData<T> from rust | |
| phantom: Optional[Callable[P, None]] | |
| def add_str(self) -> C[Concatenate[str, P]]: | |
| return C[Concatenate[str, P]]() | |
| def add_int(self) -> C[Concatenate[int, P]]: | |
| return C[Concatenate[int, P]]() | |
| def f(c: C[P]) -> None: | |
| reveal_type(c) # N: Revealed type is "__main__.C[P`-1]" | |
| n1 = c.add_str() | |
| reveal_type(n1) # N: Revealed type is "__main__.C[[builtins.str, **P`-1]]" | |
| n2 = n1.add_int() | |
| reveal_type(n2) # N: Revealed type is "__main__.C[[builtins.int, builtins.str, **P`-1]]" | |
| p1 = c.add_int() | |
| reveal_type(p1) # N: Revealed type is "__main__.C[[builtins.int, **P`-1]]" | |
| p2 = p1.add_str() | |
| reveal_type(p2) # N: Revealed type is "__main__.C[[builtins.str, builtins.int, **P`-1]]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecLiteralJoin] | |
| from typing import Generic, Callable, Union | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| class Job(Generic[_P]): | |
| def __init__(self, target: Callable[_P, None]) -> None: | |
| self.target = target | |
| def func( | |
| action: Union[Job[int], Callable[[int], None]], | |
| ) -> None: | |
| job = action if isinstance(action, Job) else Job(action) | |
| reveal_type(job) # N: Revealed type is "__main__.Job[[builtins.int]]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testApplyParamSpecToParamSpecLiterals] | |
| from typing import TypeVar, Generic, Callable | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| _R_co = TypeVar("_R_co", covariant=True) | |
| class Job(Generic[_P, _R_co]): | |
| def __init__(self, target: Callable[_P, _R_co]) -> None: | |
| self.target = target | |
| def run_job(job: Job[_P, None], *args: _P.args, **kwargs: _P.kwargs) -> None: # N: "run_job" defined here | |
| ... | |
| def func(job: Job[[int, str], None]) -> None: | |
| run_job(job, 42, "Hello") | |
| run_job(job, "Hello", 42) # E: Argument 2 to "run_job" has incompatible type "str"; expected "int" \ | |
| # E: Argument 3 to "run_job" has incompatible type "int"; expected "str" | |
| run_job(job, 42, msg="Hello") # E: Unexpected keyword argument "msg" for "run_job" | |
| run_job(job, "Hello") # E: Too few arguments for "run_job" \ | |
| # E: Argument 2 to "run_job" has incompatible type "str"; expected "int" | |
| def func2(job: Job[..., None]) -> None: | |
| run_job(job, 42, "Hello") | |
| run_job(job, "Hello", 42) | |
| run_job(job, 42, msg="Hello") | |
| run_job(job, x=42, msg="Hello") | |
| [builtins fixtures/paramspec.pyi] | |
| [case testExpandNonBareParamSpecAgainstCallable] | |
| from typing import Callable, TypeVar, Any | |
| from typing_extensions import ParamSpec | |
| CallableT = TypeVar("CallableT", bound=Callable[..., Any]) | |
| _P = ParamSpec("_P") | |
| _R = TypeVar("_R") | |
| def simple_decorator(callable: CallableT) -> CallableT: | |
| # set some attribute on 'callable' | |
| return callable | |
| class A: | |
| @simple_decorator | |
| def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: | |
| ... | |
| reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`-2, *_P.args, **_P.kwargs) -> _R`-2" | |
| reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`5, *_P.args, **_P.kwargs) -> _R`5" | |
| def f(x: int) -> int: | |
| ... | |
| reveal_type(A().func(f, 42)) # N: Revealed type is "builtins.int" | |
| # TODO: this should reveal `int` | |
| reveal_type(A().func(lambda x: x + x, 42)) # N: Revealed type is "Any" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecConstraintOnOtherParamSpec] | |
| from typing import Callable, TypeVar, Any, Generic | |
| from typing_extensions import ParamSpec | |
| CallableT = TypeVar("CallableT", bound=Callable[..., Any]) | |
| _P = ParamSpec("_P") | |
| _R_co = TypeVar("_R_co", covariant=True) | |
| def simple_decorator(callable: CallableT) -> CallableT: | |
| ... | |
| class Job(Generic[_P, _R_co]): | |
| def __init__(self, target: Callable[_P, _R_co]) -> None: | |
| ... | |
| class A: | |
| @simple_decorator | |
| def func(self, action: Job[_P, None]) -> Job[_P, None]: | |
| ... | |
| reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`-1, None]) -> __main__.Job[_P`-1, None]" | |
| reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]" | |
| reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" | |
| def f(x: int, y: int) -> None: ... | |
| reveal_type(A().func(Job(f))) # N: Revealed type is "__main__.Job[[x: builtins.int, y: builtins.int], None]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testConstraintBetweenParamSpecFunctions1] | |
| from typing import Callable, TypeVar, Any, Generic | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| _R_co = TypeVar("_R_co", covariant=True) | |
| def simple_decorator(callable: Callable[_P, _R_co]) -> Callable[_P, _R_co]: ... | |
| class Job(Generic[_P]): ... | |
| @simple_decorator | |
| def func(__action: Job[_P]) -> Callable[_P, None]: | |
| ... | |
| reveal_type(func) # N: Revealed type is "def [_P] (__main__.Job[_P`-1]) -> def (*_P.args, **_P.kwargs)" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testConstraintBetweenParamSpecFunctions2] | |
| from typing import Callable, TypeVar, Any, Generic | |
| from typing_extensions import ParamSpec | |
| CallableT = TypeVar("CallableT", bound=Callable[..., Any]) | |
| _P = ParamSpec("_P") | |
| def simple_decorator(callable: CallableT) -> CallableT: ... | |
| class Job(Generic[_P]): ... | |
| @simple_decorator | |
| def func(__action: Job[_P]) -> Callable[_P, None]: | |
| ... | |
| reveal_type(func) # N: Revealed type is "def [_P] (__main__.Job[_P`-1]) -> def (*_P.args, **_P.kwargs)" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testConstraintsBetweenConcatenatePrefixes] | |
| from typing import Any, Callable, Generic, TypeVar | |
| from typing_extensions import Concatenate, ParamSpec | |
| _P = ParamSpec("_P") | |
| _T = TypeVar("_T") | |
| class Awaitable(Generic[_T]): ... | |
| def adds_await() -> Callable[ | |
| [Callable[Concatenate[_T, _P], None]], | |
| Callable[Concatenate[_T, _P], Awaitable[None]], | |
| ]: | |
| def decorator( | |
| func: Callable[Concatenate[_T, _P], None], | |
| ) -> Callable[Concatenate[_T, _P], Awaitable[None]]: | |
| ... | |
| return decorator # we want `_T` and `_P` to refer to the same things. | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecVariance] | |
| from typing import Callable, Generic | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| class Job(Generic[_P]): | |
| def __init__(self, target: Callable[_P, None]) -> None: ... | |
| def into_callable(self) -> Callable[_P, None]: ... | |
| class A: | |
| def func(self, var: int) -> None: ... | |
| def other_func(self, job: Job[[int]]) -> None: ... | |
| job = Job(A().func) | |
| reveal_type(job) # N: Revealed type is "__main__.Job[[var: builtins.int]]" | |
| A().other_func(job) # This should NOT error (despite the keyword) | |
| # and yet the keyword should remain | |
| job.into_callable()(var=42) | |
| job.into_callable()(x=42) # E: Unexpected keyword argument "x" | |
| # similar for other functions | |
| def f1(n: object) -> None: ... | |
| def f2(n: int) -> None: ... | |
| def f3(n: bool) -> None: ... | |
| # just like how this is legal... | |
| a1: Callable[[bool], None] | |
| a1 = f3 | |
| a1 = f2 | |
| a1 = f1 | |
| # ... this is also legal | |
| a2: Job[[bool]] | |
| a2 = Job(f3) | |
| a2 = Job(f2) | |
| a2 = Job(f1) | |
| # and this is not legal | |
| def f4(n: bytes) -> None: ... | |
| a1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[bytes], None]", variable has type "Callable[[bool], None]") | |
| a2 = Job(f4) # E: Argument 1 to "Job" has incompatible type "Callable[[bytes], None]"; expected "Callable[[bool], None]" | |
| # nor is this: | |
| a4: Job[[int]] | |
| a4 = Job(f3) # E: Argument 1 to "Job" has incompatible type "Callable[[bool], None]"; expected "Callable[[int], None]" | |
| a4 = Job(f2) | |
| a4 = Job(f1) | |
| # just like this: | |
| a3: Callable[[int], None] | |
| a3 = f3 # E: Incompatible types in assignment (expression has type "Callable[[bool], None]", variable has type "Callable[[int], None]") | |
| a3 = f2 | |
| a3 = f1 | |
| [builtins fixtures/paramspec.pyi] | |
| [case testDecoratingClassesThatUseParamSpec] | |
| from typing import Generic, TypeVar, Callable, Any | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| _T = TypeVar("_T") | |
| _F = TypeVar("_F", bound=Callable[..., Any]) | |
| def f(x: _F) -> _F: ... | |
| @f # Should be ok | |
| class OnlyParamSpec(Generic[_P]): | |
| pass | |
| @f # Should be ok | |
| class MixedWithTypeVar1(Generic[_P, _T]): | |
| pass | |
| @f # Should be ok | |
| class MixedWithTypeVar2(Generic[_T, _P]): | |
| pass | |
| [builtins fixtures/dict.pyi] | |
| [case testGenericsInInferredParamspec] | |
| from typing import Callable, TypeVar, Generic | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| _T = TypeVar("_T") | |
| class Job(Generic[_P]): | |
| def __init__(self, target: Callable[_P, None]) -> None: ... | |
| def into_callable(self) -> Callable[_P, None]: ... | |
| def generic_f(x: _T) -> None: ... | |
| j = Job(generic_f) | |
| reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" | |
| jf = j.into_callable() | |
| reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1)" | |
| reveal_type(jf(1)) # N: Revealed type is "None" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testGenericsInInferredParamspecReturn] | |
| # flags: --new-type-inference | |
| from typing import Callable, TypeVar, Generic | |
| from typing_extensions import ParamSpec | |
| _P = ParamSpec("_P") | |
| _T = TypeVar("_T") | |
| class Job(Generic[_P, _T]): | |
| def __init__(self, target: Callable[_P, _T]) -> None: ... | |
| def into_callable(self) -> Callable[_P, _T]: ... | |
| def generic_f(x: _T) -> _T: ... | |
| j = Job(generic_f) | |
| reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1], _T`-1]" | |
| jf = j.into_callable() | |
| reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1) -> _T`-1" | |
| reveal_type(jf(1)) # N: Revealed type is "builtins.int" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testStackedConcatenateIsIllegal] | |
| from typing_extensions import Concatenate, ParamSpec | |
| from typing import Callable | |
| P = ParamSpec("P") | |
| def x(f: Callable[Concatenate[int, Concatenate[int, P]], None]) -> None: ... # E: Nested Concatenates are invalid | |
| [builtins fixtures/paramspec.pyi] | |
| [case testPropagatedAnyConstraintsAreOK] | |
| from typing import Any, Callable, Generic, TypeVar | |
| from typing_extensions import ParamSpec | |
| T = TypeVar("T") | |
| P = ParamSpec("P") | |
| def callback(func: Callable[[Any], Any]) -> None: ... | |
| class Job(Generic[P]): ... | |
| @callback | |
| def run_job(job: Job[...]) -> T: ... # E: A function returning TypeVar should receive at least one argument containing the same TypeVar | |
| [builtins fixtures/tuple.pyi] | |
| [case testTupleAndDictOperationsOnParamSpecArgsAndKwargs] | |
| from typing import Callable, Iterator, Iterable, TypeVar, Tuple | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec('P') | |
| T = TypeVar('T') | |
| def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ... | |
| def func(callback: Callable[P, str]) -> Callable[P, str]: | |
| def inner(*args: P.args, **kwargs: P.kwargs) -> str: | |
| reveal_type(args[5]) # N: Revealed type is "builtins.object" | |
| for a in args: | |
| reveal_type(a) # N: Revealed type is "builtins.object" | |
| for idx, a in enumerate(args): | |
| reveal_type(idx) # N: Revealed type is "builtins.int" | |
| reveal_type(a) # N: Revealed type is "builtins.object" | |
| b = 'foo' in args | |
| reveal_type(b) # N: Revealed type is "builtins.bool" | |
| reveal_type(args.count(42)) # N: Revealed type is "builtins.int" | |
| reveal_type(len(args)) # N: Revealed type is "builtins.int" | |
| for c, d in kwargs.items(): | |
| reveal_type(c) # N: Revealed type is "builtins.str" | |
| reveal_type(d) # N: Revealed type is "builtins.object" | |
| kwargs.pop('bar') | |
| return 'baz' | |
| return inner | |
| [builtins fixtures/paramspec.pyi] | |
| [case testUnpackingParamsSpecArgsAndKwargs] | |
| from typing import Callable | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec("P") | |
| def func(callback: Callable[P, str]) -> Callable[P, str]: | |
| def inner(*args: P.args, **kwargs: P.kwargs) -> str: | |
| a, *b = args | |
| reveal_type(a) # N: Revealed type is "builtins.object" | |
| reveal_type(b) # N: Revealed type is "builtins.list[builtins.object]" | |
| c, *d = kwargs | |
| reveal_type(c) # N: Revealed type is "builtins.str" | |
| reveal_type(d) # N: Revealed type is "builtins.list[builtins.str]" | |
| e = {**kwargs} | |
| reveal_type(e) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" | |
| return "foo" | |
| return inner | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecArgsAndKwargsMissmatch] | |
| from typing import Callable | |
| from typing_extensions import ParamSpec | |
| P1 = ParamSpec("P1") | |
| def func(callback: Callable[P1, str]) -> Callable[P1, str]: | |
| def inner( | |
| *args: P1.kwargs, # E: Use "P1.args" for variadic "*" parameter | |
| **kwargs: P1.args, # E: Use "P1.kwargs" for variadic "**" parameter | |
| ) -> str: | |
| return "foo" | |
| return inner | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecTestPropAccess] | |
| from typing import Callable | |
| from typing_extensions import ParamSpec | |
| P1 = ParamSpec("P1") | |
| def func1(callback: Callable[P1, str]) -> Callable[P1, str]: | |
| def inner( | |
| *args: P1.typo, # E: Use "P1.args" for variadic "*" parameter \ | |
| # E: Name "P1.typo" is not defined | |
| **kwargs: P1.kwargs, | |
| ) -> str: | |
| return "foo" | |
| return inner | |
| def func2(callback: Callable[P1, str]) -> Callable[P1, str]: | |
| def inner( | |
| *args: P1.args, | |
| **kwargs: P1.__bound__, # E: Use "P1.kwargs" for variadic "**" parameter \ | |
| # E: Name "P1.__bound__" is not defined | |
| ) -> str: | |
| return "foo" | |
| return inner | |
| def func3(callback: Callable[P1, str]) -> Callable[P1, str]: | |
| def inner( | |
| *args: P1.__bound__, # E: Use "P1.args" for variadic "*" parameter \ | |
| # E: Name "P1.__bound__" is not defined | |
| **kwargs: P1.invalid, # E: Use "P1.kwargs" for variadic "**" parameter \ | |
| # E: Name "P1.invalid" is not defined | |
| ) -> str: | |
| return "foo" | |
| return inner | |
| [builtins fixtures/paramspec.pyi] | |
| [case testInvalidParamSpecDefinitionsWithArgsKwargs] | |
| from typing import Callable, ParamSpec | |
| P = ParamSpec('P') | |
| def c1(f: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> int: ... | |
| def c2(f: Callable[P, int]) -> int: ... | |
| def c3(f: Callable[P, int], *args, **kwargs) -> int: ... | |
| # It is ok to define, | |
| def c4(f: Callable[P, int], *args: int, **kwargs: str) -> int: | |
| # but not ok to call: | |
| f(*args, **kwargs) # E: Argument 1 has incompatible type "*Tuple[int, ...]"; expected "P.args" \ | |
| # E: Argument 2 has incompatible type "**Dict[str, str]"; expected "P.kwargs" | |
| return 1 | |
| def f1(f: Callable[P, int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f2(f: Callable[P, int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f3(f: Callable[P, int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f4(f: Callable[P, int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| # Error message test: | |
| P1 = ParamSpec('P1') | |
| def m1(f: Callable[P1, int], *a, **k: P1.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testInvalidParamSpecAndConcatenateDefinitionsWithArgsKwargs] | |
| from typing import Callable, ParamSpec | |
| from typing_extensions import Concatenate | |
| P = ParamSpec('P') | |
| def c1(f: Callable[Concatenate[int, P], int], *args: P.args, **kwargs: P.kwargs) -> int: ... | |
| def c2(f: Callable[Concatenate[int, P], int]) -> int: ... | |
| def c3(f: Callable[Concatenate[int, P], int], *args, **kwargs) -> int: ... | |
| # It is ok to define, | |
| def c4(f: Callable[Concatenate[int, P], int], *args: int, **kwargs: str) -> int: | |
| # but not ok to call: | |
| f(1, *args, **kwargs) # E: Argument 2 has incompatible type "*Tuple[int, ...]"; expected "P.args" \ | |
| # E: Argument 3 has incompatible type "**Dict[str, str]"; expected "P.kwargs" | |
| return 1 | |
| def f1(f: Callable[Concatenate[int, P], int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f2(f: Callable[Concatenate[int, P], int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f3(f: Callable[Concatenate[int, P], int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f4(f: Callable[Concatenate[int, P], int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testValidParamSpecInsideGenericWithoutArgsAndKwargs] | |
| from typing import Callable, ParamSpec, Generic | |
| from typing_extensions import Concatenate | |
| P = ParamSpec('P') | |
| class Some(Generic[P]): ... | |
| def create(s: Some[P], *args: int): ... | |
| def update(s: Some[P], **kwargs: int): ... | |
| def delete(s: Some[P]): ... | |
| def from_callable1(c: Callable[P, int], *args: int, **kwargs: int) -> Some[P]: ... | |
| def from_callable2(c: Callable[P, int], **kwargs: int) -> Some[P]: ... | |
| def from_callable3(c: Callable[P, int], *args: int) -> Some[P]: ... | |
| def from_extra1(c: Callable[Concatenate[int, P], int], *args: int, **kwargs: int) -> Some[P]: ... | |
| def from_extra2(c: Callable[Concatenate[int, P], int], **kwargs: int) -> Some[P]: ... | |
| def from_extra3(c: Callable[Concatenate[int, P], int], *args: int) -> Some[P]: ... | |
| [builtins fixtures/paramspec.pyi] | |
| [case testUnboundParamSpec] | |
| from typing import Callable, ParamSpec | |
| P1 = ParamSpec('P1') | |
| P2 = ParamSpec('P2') | |
| def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| def f1(*args: P1.args): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| def f2(**kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| def f3(*args: P1.args, **kwargs: int): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| def f4(*args: int, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| # Error message is based on the `args` definition: | |
| def f5(*args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" | |
| def f6(*args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| # Multiple `ParamSpec` variables can be found, they should not affect error message: | |
| P3 = ParamSpec('P3') | |
| def f7(first: Callable[P3, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" | |
| def f8(first: Callable[P3, int], *args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testArgsKwargsWithoutParamSpecVar] | |
| from typing import Generic, Callable, ParamSpec | |
| P = ParamSpec('P') | |
| # This must be allowed: | |
| class Some(Generic[P]): | |
| def call(self, *args: P.args, **kwargs: P.kwargs): ... | |
| # TODO: this probably should be reported. | |
| def call(*args: P.args, **kwargs: P.kwargs): ... | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecInferenceCrash] | |
| from typing import Callable, Generic, ParamSpec, TypeVar | |
| def foo(x: int) -> int: ... | |
| T = TypeVar("T") | |
| def bar(x: T) -> T: ... | |
| P = ParamSpec("P") | |
| class C(Generic[P]): | |
| def __init__(self, fn: Callable[P, int], *args: P.args, **kwargs: P.kwargs): ... | |
| reveal_type(bar(C(fn=foo, x=1))) # N: Revealed type is "__main__.C[[x: builtins.int]]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecClassConstructor] | |
| # flags: --strict-optional | |
| from typing import ParamSpec, Callable | |
| P = ParamSpec("P") | |
| class SomeClass: | |
| def __init__(self, a: str) -> None: | |
| pass | |
| def func(t: Callable[P, SomeClass], val: Callable[P, SomeClass]) -> None: | |
| pass | |
| def constructor(a: str) -> SomeClass: | |
| return SomeClass(a) | |
| def wrong_constructor(a: bool) -> SomeClass: | |
| return SomeClass("a") | |
| func(SomeClass, constructor) | |
| func(SomeClass, wrong_constructor) # E: Argument 1 to "func" has incompatible type "Type[SomeClass]"; expected "Callable[[VarArg(<nothing>), KwArg(<nothing>)], SomeClass]" \ | |
| # E: Argument 2 to "func" has incompatible type "Callable[[bool], SomeClass]"; expected "Callable[[VarArg(<nothing>), KwArg(<nothing>)], SomeClass]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecInTypeAliasBasic] | |
| from typing import ParamSpec, Callable | |
| P = ParamSpec("P") | |
| C = Callable[P, int] | |
| def f(n: C[P]) -> C[P]: ... | |
| @f | |
| def bar(x: int) -> int: ... | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[int], str]"; expected "Callable[[int], int]" | |
| def foo(x: int) -> str: ... | |
| x: C[[int, str]] | |
| reveal_type(x) # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int" | |
| y: C[int, str] | |
| reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecInTypeAliasConcatenate] | |
| from typing import ParamSpec, Callable | |
| from typing_extensions import Concatenate | |
| P = ParamSpec("P") | |
| C = Callable[Concatenate[int, P], int] | |
| def f(n: C[P]) -> C[P]: ... | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[], int]"; expected "Callable[[int], int]" | |
| def bad() -> int: ... | |
| @f | |
| def bar(x: int) -> int: ... | |
| @f | |
| def bar2(x: int, y: str) -> int: ... | |
| reveal_type(bar2) # N: Revealed type is "def (builtins.int, y: builtins.str) -> builtins.int" | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[int], str]"; expected "Callable[[int], int]" \ | |
| # N: This is likely because "foo" has named arguments: "x". Consider marking them positional-only | |
| def foo(x: int) -> str: ... | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[str, int], int]"; expected "Callable[[int, int], int]" \ | |
| # N: This is likely because "foo2" has named arguments: "x". Consider marking them positional-only | |
| def foo2(x: str, y: int) -> int: ... | |
| x: C[[int, str]] | |
| reveal_type(x) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str) -> builtins.int" | |
| y: C[int, str] | |
| reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str) -> builtins.int" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecInTypeAliasRecursive] | |
| from typing import ParamSpec, Callable, Union | |
| P = ParamSpec("P") | |
| C = Callable[P, Union[int, C[P]]] | |
| def f(n: C[P]) -> C[P]: ... | |
| @f | |
| def bar(x: int) -> int: ... | |
| @f | |
| def bar2(__x: int) -> Callable[[int], int]: ... | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[int], str]"; expected "C[[int]]" | |
| def foo(x: int) -> str: ... | |
| @f # E: Argument 1 to "f" has incompatible type "Callable[[int], Callable[[int], str]]"; expected "C[[int]]" | |
| def foo2(__x: int) -> Callable[[int], str]: ... | |
| x: C[[int, str]] | |
| reveal_type(x) # N: Revealed type is "def (builtins.int, builtins.str) -> Union[builtins.int, ...]" | |
| y: C[int, str] | |
| reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str) -> Union[builtins.int, ...]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecAliasInRuntimeContext] | |
| from typing import ParamSpec, Generic | |
| P = ParamSpec("P") | |
| class C(Generic[P]): ... | |
| c = C[int, str]() | |
| reveal_type(c) # N: Revealed type is "__main__.C[[builtins.int, builtins.str]]" | |
| A = C[P] | |
| a = A[int, str]() | |
| reveal_type(a) # N: Revealed type is "__main__.C[[builtins.int, builtins.str]]" | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecAliasInvalidLocations] | |
| from typing import ParamSpec, Generic, List, TypeVar, Callable | |
| P = ParamSpec("P") | |
| T = TypeVar("T") | |
| A = List[T] | |
| def f(x: A[[int, str]]) -> None: ... # E: Bracketed expression "[...]" is not valid as a type \ | |
| # N: Did you mean "List[...]"? | |
| def g(x: A[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ | |
| # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' | |
| C = Callable[P, T] | |
| x: C[int] # E: Bad number of arguments for type alias, expected: 2, given: 1 | |
| y: C[int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" | |
| z: C[int, str, bytes] # E: Bad number of arguments for type alias, expected: 2, given: 3 | |
| [builtins fixtures/paramspec.pyi] | |
| [case testTrivialParametersHandledCorrectly] | |
| from typing import ParamSpec, Generic, TypeVar, Callable, Any | |
| from typing_extensions import Concatenate | |
| P = ParamSpec("P") | |
| T = TypeVar("T") | |
| S = TypeVar("S") | |
| class C(Generic[S, P, T]): ... | |
| def foo(f: Callable[P, int]) -> None: | |
| x: C[Any, ..., Any] | |
| x1: C[int, Concatenate[int, str, P], str] | |
| x = x1 # OK | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecAliasNested] | |
| from typing import ParamSpec, Callable, List, TypeVar, Generic | |
| from typing_extensions import Concatenate | |
| P = ParamSpec("P") | |
| A = List[Callable[P, None]] | |
| B = List[Callable[Concatenate[int, P], None]] | |
| fs: A[int, str] | |
| reveal_type(fs) # N: Revealed type is "builtins.list[def (builtins.int, builtins.str)]" | |
| gs: B[int, str] | |
| reveal_type(gs) # N: Revealed type is "builtins.list[def (builtins.int, builtins.int, builtins.str)]" | |
| T = TypeVar("T") | |
| class C(Generic[T]): ... | |
| C[Callable[P, int]]() # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ | |
| # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas | |
| [builtins fixtures/paramspec.pyi] | |
| [case testConcatDeferralNoCrash] | |
| from typing import Callable, TypeVar | |
| from typing_extensions import Concatenate, ParamSpec | |
| P = ParamSpec("P") | |
| T = TypeVar("T", bound="Defer") | |
| Alias = Callable[P, bool] | |
| Concat = Alias[Concatenate[T, P]] | |
| def test(f: Concat[T, ...]) -> None: ... | |
| class Defer: ... | |
| [builtins fixtures/paramspec.pyi] | |
| [case testNoParamSpecDoubling] | |
| # https://github.com/python/mypy/issues/12734 | |
| from typing import Callable, ParamSpec | |
| from typing_extensions import Concatenate | |
| P = ParamSpec("P") | |
| Q = ParamSpec("Q") | |
| def foo(f: Callable[P, int]) -> Callable[P, int]: | |
| return f | |
| def bar(f: Callable[Concatenate[str, Q], int]) -> Callable[Concatenate[str, Q], int]: | |
| return foo(f) | |
| [builtins fixtures/paramspec.pyi] | |
| [case testAlreadyExpandedCallableWithParamSpecReplacement] | |
| from typing import Callable, Any, overload | |
| from typing_extensions import Concatenate, ParamSpec | |
| P = ParamSpec("P") | |
| @overload | |
| def command() -> Callable[[Callable[Concatenate[object, object, P], object]], None]: # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | |
| ... | |
| @overload | |
| def command( | |
| cls: int = ..., | |
| ) -> Callable[[Callable[Concatenate[object, P], object]], None]: | |
| ... | |
| def command( | |
| cls: int = 42, | |
| ) -> Any: | |
| ... | |
| [builtins fixtures/paramspec.pyi] | |
| [case testCopiedParamSpecComparison] | |
| # minimized from https://github.com/python/mypy/issues/12909 | |
| from typing import Callable | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec("P") | |
| def identity(func: Callable[P, None]) -> Callable[P, None]: ... | |
| @identity | |
| def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... | |
| [builtins fixtures/paramspec.pyi] | |
| [case testParamSpecDecoratorAppliedToGeneric] | |
| # flags: --new-type-inference | |
| from typing import Callable, List, TypeVar | |
| from typing_extensions import ParamSpec | |
| P = ParamSpec("P") | |
| T = TypeVar("T") | |
| U = TypeVar("U") | |
| def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ... | |
| 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 [U] (x: U`-1) -> builtins.list[U`-1]" | |
| [builtins fixtures/paramspec.pyi] |