Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

How can I type annotate a factory helper's **kwargs to match that of the __init__ of the type being created? #1775

Unanswered
cjw296 asked this question in Q&A
Discussion options

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?

You must be logged in to vote

Replies: 2 comments 15 replies

Comment options

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: ...
You must be logged in to vote
14 replies
Comment options

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...

Comment options

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.

Comment options

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

Comment options

Correct

Comment options

Hmm, unfortunately, pytype seems to trip up on much simpler stuff that mypy is happy with :-/

Comment options

#1500 seems to be the same ballpark as this.

You must be logged in to vote
1 reply
Comment options

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /