Repository navigation
Type Aliases that are generic over ParamSpec don't work #11855
Description
Activity
This is a bit annoying to do so far, as we have to return a CallableType as a type alias but... like... we need to mark that it is missing a ParamSpec. This is OK with TypeVars because
[<unbound T>]is perfectly fine (representation wise), but there's no way to represent that a ParamSpec is unbound because CallableType expects some arguments which is aaaaaaaaI think the best way will be to make essentially a sentinel value and check for that, though it's been annoying to do so far.
- addedtopic-paramspecPEP 612, ParamSpec, ConcatenatePEP 612, ParamSpec, Concatenate
on Feb 20, 2022 Another example with
Concatenateinside a Type Alias. Saw it in the last pyright release. microsoft/pyright@2f5046bimport asyncio from typing import Coroutine, Any, TypeVar, Concatenate, Callable, ParamSpec _T = TypeVar("_T") _P = ParamSpec("_P") Coro = Coroutine[Any, Any, _T] CoroFunc = Callable[_P, Coro[_T]] class ClassA: ... CheckFunc = CoroFunc[Concatenate[ClassA, _P], bool] async def my_check_func(obj: ClassA, a: int, b: str) -> bool: print(a, b) return str(a) == b async def takes_check_func( check_func: CheckFunc[_P], *args: _P.args, **kwargs: _P.kwargs ): await check_func(ClassA(), *args, **kwargs) asyncio.run(takes_check_func(my_check_func, 1, "2")) # This should generate an error because the signature doesn't match. asyncio.run(takes_check_func(my_check_func, 1, 2))
- addedtopic-type-aliasTypeAlias and other type alias issuesTypeAlias and other type alias issues
on Apr 4, 2022 This may also be related?
https://mypy-play.net/?mypy=latest&python=3.10&gist=ae5f16e9b9ca9346f8a28ab60bd9825f
from typing import ParamSpec, Callable, Optional, TypeVar P = ParamSpec("P") R = TypeVar("R") x: Callable[P, R] # error y: Optional[Callable[P, R]] # error z: Callable[..., R] # fine
main.py:6: error: The first argument to Callable must be a list of types or "..." main.py:7: error: The first argument to Callable must be a list of types or "..." Found 2 errors in 1 file (checked 1 source file)Reacted by Daniel Fainberg, Abid H. Mujtaba, Rehan Rajput and Giovanni CerretaniAlso this:
https://mypy-play.net/?mypy=latest&python=3.10&gist=c4d6f13911151a073aed0d6650e4af3e
from typing import ParamSpec, Callable, Optional, TypeVar P = ParamSpec("P") R = TypeVar("R") def target_func(x: int) -> int: return 0 def test(x: Callable[P, R]): pass def test_optional(x: Optional[Callable[P, R]] = None): pass test(target_func) # fine test_optional(target_func) # error
main.py:16: error: Argument 1 to "test_optional" has incompatible type "Callable[[int], int]"; expected "Optional[Callable[[VarArg(<nothing>), KwArg(<nothing>)], <nothing>]]" Found 1 error in 1 file (checked 1 source file)- changed the title
[-]Type Aliases for ParamSpec don't work[/-][+]Type Aliases that are generic over ParamSpec don't work[/+]on May 23, 2022 In my case I was trying to annotate a decorator, e.g.
P = ParamSpec("P") R = TypeVar("R") C = Callable[P, R] D = Callable[[C], C] @overload def my_decorator(arg: logging.Logger, level: str= "debug") -> D: ... @overload def my_decorator(arg: C, /) -> C: ... def my_decorator(arg: Union[C, logging.Logger], level: str= "debug") -> Union[C, D]: ...instead of
P = ParamSpec("P") R = TypeVar("R") def my_decorator(arg: Union[Callable[P, R], logging.Logger], level: str= "debug" ) -> Union[Callable[[Callable[P, R]], Callable[P, R]], Callable[P, R]]:which could be used as
@my_decorator def my_function(): passor
@my_decorator(logger, level="info") def my_function(): passHaving an alias would make everything much simpler.
PS: Would be cool as well if an unbound
Callablehad implicitParamSpec("P")andTypeVar("R")as return value by default, e.g., this could work:C = Callable def my_decorator(arg: Union[C, logging.Logger], level: str= "debug") -> Union[Callable[[C], C], C]: def wrapper0(fn0: Callable[C.params, C.returns]) -> C.returns: .... def wrapper1(*args: C.params.args, **kwargs: C.params.kwargs]) -> C.returns: ....But that would require a PEP.
Reacted by Maximilian RoosAnother case which also throws another funny error on
Concatenate(?)from typing import ParamSpec, Callable, Concatenate class Event: ... P = ParamSpec("P") EventHandler = Callable[Concatenate[Event, P], None]
main.py:7: error: The first argument to Callable must be a list of types, parameter specification, or "..." main.py:7: note: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas main.py:7: error: The last parameter to Concatenate needs to be a ParamSpec main.py:7: error: ParamSpec "P" is unbound Found 3 errors in 1 file (checked 1 source file)Reacted by bersbersbersI'm experiencing this issue too, is there a workaround for this?
Reacted by Ellis BreenReacted by bersbersbersPossible MFE (minimum failing example):
from typing import Callable, ParamSpec P = ParamSpec("P") FunctionType = Callable[P, None]
main.py:4: error: The first argument to Callable must be a list of types, parameter specification, or "..." main.py:4: note: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas Found 1 error in 1 file (checked 1 source file)
The issue I reported in #14069 is likely a symptom of this issue (and lack of alias support)
- added a commit that references this issue
on Nov 24, 2022 Is there any information on when this fix would be available? (When 0.992 is released?)
Is there any information on when this fix would be available? (When 0.992 is released?)
You can track progress on the next release by following #13685 (I think it's unlikely that there'll be a 0.992 release).
The following is not working for me in
1.0.0:from typing import TypeAlias, ParamSpec, Concatenate, Callable P = ParamSpec("P") intfun: TypeAlias = Callable[Concatenate[int, P], None] def foo(i: int) -> None: pass a: intfun = foo # ✘ Incompatible types in assignment
@randolf-scholz, you have defined a generic type alias that has a single type parameter (
P), but when you use the type alias in the last line, you're not specifying a type argument. Mypy should assume...in this case (just like it assumesAnyfor a regular type variable). If you explicitly supply the type argument (a: intfun[...]ora: intfun[[]]), mypy succeeds.So I agree this is a bug in mypy, but your code is also probably not doing what you intended. You might want to file a separate bug since this one has been closed for a while.
Reacted by Alex Waygood and Randolf Scholz@erictraut So when I use
intfun[...], thenmypyreveals the type asdef (builtins.int, *Any, **Any). Is there any way to directly specify this exact type hint as aTypeAlias? It seems weird that one has to go this roundabout way, as neitherCallable[[int, ...], None], norCallable[Concatenate[int, ...], None]are supported.I recommend using a callback protocol
@hauntsaninja Not sure how that would look like, when one writes a Callback-Protocol, how do you make the signature generic? I created a mypy-play with a bunch of test-cases here:
- https://gist.github.com/2ba6f03063086aec23f6a6cad11d28fc
- https://mypy-play.net/?mypy=latest&python=3.11&gist=2ba6f03063086aec23f6a6cad11d28fc
- Cannot instantiate type "Type[Callable[[int], int]]" [misc] typing#1346
EDIT: Found some older threads about this
Reacted by ShantanuOh thanks, I see. Basically the Concatenate version of #5876
Bug Report
I expect
n = Callable[P, int]to work, or at leastn: TypeAlias = Callable[P, int]To Reproduce
Expected Behavior
No errors
Actual Behavior
Your Environment
mypy playground