-
Notifications
You must be signed in to change notification settings - Fork 287
-
So, for this code:
class Collection: def make(self, type_: Type[T], **attrs) -> T: ...
How can I annotate **attrs such that a type checker, mypy in particular, will correctly ensure that any keywords passed are a valid subset of those that could be passed to T's constructor?
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 2 comments 15 replies
-
You can use a ParamSpec. There's no way to constrain it to only keyword parameters though. After all, the constructor may support positional parameters.
from typing import Callable class Collection: def make[**P, T]( self, type_: Callable[P, T], *args: P.args, **kwargs: P.kwargs ) -> T: ...
Here's the same thing using older (pre-3.12) syntax:
from typing import Callable, ParamSpec, TypeVar P = ParamSpec("P") T = TypeVar("T") class Collection: def make(self, type_: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ...
Beta Was this translation helpful? Give feedback.
All reactions
-
but that wouldn't necessarily change the requirement that you always need to unpack both components of P.
Sorry, not following this, could you provide a code example of what you mean?
So the type checker would ensure that no positional arguments can be passed in, but your generic signature would still need to include them anyways
This sounds pretty sub-optimal :-/
What's pytype? That's not a phrase I've heard before...
Beta Was this translation helpful? Give feedback.
All reactions
-
Sorry, not following this, could you provide a code example of what you mean?
The spec for ParamSpec currently states that if P.args is used in a function's signature P.kwargs must also be used and vice versa, there also can't be any arguments in between, the only thing you're allowed to do is prepend arguments, like you would with Concatenate. This is to ensure that no matter what P solves to, the call is still possible. Otherwise the same P would need to check every single place it's used in order to ensure consistency.
def allowed[**P](self, *args: P.args, **kwargs: P.kwargs): ... def positional_only_prepend_allowed[**P](self, arg: int, /, *args: P.args, **kwargs: P.kwargs): ... def no_kwargs_disallowed[**P](self, *args: P.args): ... def no_args_disallowed[**P](self, **kwargs: P.kwargs): ... def middle_extra_kwarg_disallowed[**P](self, *args: P.args, my_new_kwarg: int = 0, **kwargs: P.kwargs): ...
With upper bounds on P this restriction could potentially be relaxed in some cases, e.g. with an upper bound of def (**kwargs: Any) you may be allowed to omit P.args and define an arbitrary number of preceding positional only arguments. Although the required implementation complexity makes this probably almost certainly not worth it.
What's pytype? That's not a phrase I've heard before...
It's a Python type checker made by Google. It performs very deep type inference and allows a lot of things other type checkers would disallow because their inference is too shallow to ensure type safety without an explicit type annotation. So it can be useful in code that doesn't make heavy use of type annotations, since it will still be able to detect a lot of problems anyways.
Beta Was this translation helpful? Give feedback.
All reactions
-
The irony's not lost on me that some of the things that have made Python so powerful over the last two or three decades end up being the things that are hardest to express in static typing...
Thanks for the pytype pointer; is this one, right? https://github.com/google/pytype
Beta Was this translation helpful? Give feedback.
All reactions
-
Correct
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Hmm, unfortunately, pytype seems to trip up on much simpler stuff that mypy is happy with :-/
Beta Was this translation helpful? Give feedback.
All reactions
-
#1500 seems to be the same ballpark as this.
Beta Was this translation helpful? Give feedback.
All reactions
-
That sounds more like a proxy type to me, which has been requested many times before, e.g. #802 but we don't have anything like that yet.
There definitely are some open questions for proxy types, like should a proxy be allowed to stand-in for the real type, what about partial proxies that don't proxy everything etc. It would definitely need a PEP to explore and answer some of those questions, so we have a concrete proposal that can be implemented in a type checker as a proof of concept.
Beta Was this translation helpful? Give feedback.