You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow using TypedDict for more precise typing of **kwds #4441
There are some situations where a user wants to have more precisely typed **kwds. Current syntax only allows homogeneous **kwds:
deffun(x: int, *, **options: str) ->None:
...
However, in situations with many heterogeneous options listing all options in the signature could be verbose and will require rewriting some existing code. There is a vague idea to allow TypedDict for such situations. For example:
Maybe for such cases the TypedDict used should be automatically understood as defined with total=False. Also it is worth mentioning that this feature will allow reusing the TypedDicts in modules where several functions have same (or similar) option sets.
The proposed syntax is problematic, as it could also mean a homogeneous **options where each value is an Options. We could work around this with some extra syntax, but it could look messy. Here's an idea:
Expand[...] could also be used for variadic type variables in case we add support for them (for example, Callable[[int, Expand[X]], None] if X is a variadic type variable).
I don't like the idea of implicitly assuming total=False.
Reacted by Ellis Breen, Neil Girdhar, Brenton Partridge, Byeonghoon Yoo, Lucas Menicucci, James, David C., Alex Rattray and Redowan Delowar
Yes, the syntax would be ambiguous. Introducing Expand only for this purpose may be not worth it, but using it also for variadic type variables is a good idea (and it feels natural).
I don't like the idea of implicitly assuming total=False.
OK, let's then drop this. In any case it is easy to add it explicitly (I think it will be used often, since keyword arguments are often optional).
@ilevkivskyi@JukkaL I'm in pretty desperate need of this so I might be able to justify working on this to my employer. Do you have any ideas about how to implement this? If not, are there areas of the code-base that I should look at in order to understand how to approach the problem?
Although this is not super tricky feature, it may be not the best choice for the first contribution, unless you like to live dangerously :-)
You will need to add a new special form Expand[...] to mypy_extensions (where TypedDict lives). This should be allowed only as annotation to **kwars, and must have only one type parameter which is a TypedDict. Then look at all things that contain words formal_to_actual and/or actual_to_formal, they might need to be updated.
Also it may be not the best time to do this, since we are in the middle of a big refactoring of semantic analyzer (one of the first steps in mypy logic, that binds names to symbols). It will take two-three more weeks to finish. Otherwise you would need to repeat some parts of logic twice: in mypy/semanal.py and in mypy/newsemanal/semanal.py.
I got around to hacking something together in #7051. It's obviously very rough but hopefully I can build something off of that.
BTW, I think Expand completely breaks the typesystem. What does this do?
x: Expand[dict[str, int]] =42
You could argue that this should work because having a type that just works for **kwargs is ugly. You could also argue that this is nonsense and should not be allowed.
I think Expand cannot be called a type. IMO the proper solution would be to introduce a breaking change in the typesystem and make **kwargs: X expand X automatically. Code that looks like this right now:
deffoo(**kwargs: int): ...
would have to be changed to:
deffoo(**kwargs: Dict[str, int]): ...
Reacted by Janka Uryga, Anton Agestam, herr kaste, Kyle Agronick, James Adams, MustNull, Charlie Denton, Bryan Helmig, Nils K, Thijs Damsma and 6 more
If you don't provide bar as a kwarg, it is an error because bar is a required field in the TypedDict.
Ah but we can specify totality like:
class MyKwargs(TypedDict, total=False):
foo: str
bar: int
So bar or any key is not required anymore. Does Unpack still report this as an error?
Unpack does not support Union.
I now realize it's not Unpack that should support Union, it should be TypedDict. And TypedDict already does. MyTypedDict | OtherTypedDict means either of the two, not combining them. Maybe what I meant was like Unpack[MyTypedDict, OtherTypedDict] where it unpacks multiple TypedDicts. But I guess that would complicate things.
@krystean Just to add to what you said, a small thing I found out yesterday is that you can also use Required and NotRequired for TypedDict if you want to have more control on individual keys (total= is for all keys of the typed dict).
See PEP-655 (also available via typing_extensions).
I am interested in sponsoring work on support for Unpack[T] in mypy if anyone is interested in putting up a PR for this and would find sponsorship helpful.
/home/devmessias/phd/pkgs/pyastrx-proj/pyastrx-my/pyright.py
/home/devmessias/phd/pkgs/pyastrx-proj/pyastrx-my/pyright.py:26:15 - error: No parameter named "c" (reportGeneralTypeIssues)
/home/devmessias/phd/pkgs/pyastrx-proj/pyastrx-my/pyright.py:27:17 - error: Argument of type"Literal[2]" cannot be assigned to parameter "a" of type"str"infunction"my_func""Literal[2]" is incompatible with "str" (reportGeneralTypeIssues)
2 errors, 0 warnings, 0 informations
Completed in 0.877sec
I am interested in sponsoring work on support for Unpack[T] in mypy if anyone is interested in putting up a PR for this and would find sponsorship helpful.
Would be great to have unpack available in mypy
Reacted by Tony Narlock, Arseniy Terekhin and Kevin Kirsche
P.S. if I understand correctly, you want to be able to take dictionary data,, and hydrate models / dataclasses via **kw. Is that an accurate depiction of what you're getting at?
@tony i don't want to hydrate, i want to correctly type helper functions for querying, waiting and other actions that replicate all if not most fields of types
It seems Unpack from typing_extensions does more than what PEP 646 originally proposed (i.e. originally only for *args/TypeVarTuple, not also **kwargs/TypedDict). It now seems to also do what Expand was proposed for in PEP 589. This is perfectly fine by me (I love having Unpack), but I have some questions:
Is Expand in PEP 646 also going to be implemented?
Will PEP 646 be updated to indicate that Unpack may also be used for variadic TypeDicts?
I think ideally there should only be 1 name and it should work for both types. (It would be confusing, for instance, to have both Expand and Unpack).
ASIDE:
If it is agreed there should be one name, my vote is for Unpack, simply because it's already implemented and the terminology matches Python terminology (we speak of "packing"/"unpacking" variables, rather than "collapsing"/"expanding" variables).
PEP 646 appears to have implemented a grammar change to utilize starred expressions in indeces beginning in Python 3.11. Could this be expanded to also implement double-starred expressions? There've been discussions here that implementing a ** syntax would require a grammar change. I'm wondering if expanding the grammar change for * would make this less work? This would be a nice-to-have (much like how the grammar was updated to support built-ins in the syntax directly rather than requiring from typing import UppercaseBuiltin and pipes for Union).
There is a new PEP 692 underway for this functionality. It proposes to add ** syntax and use Unpack for backward compatibility. The new PEP is under review, so if you want to comment on it, I'm sure the feedback would be welcome.
Pyright has provisional support for PEP 692 if you want to play with it.
A few additional notes...
PEP 589 didn't specify Expand. The only reference to Expand is in the rejected alternatives section. There are currently no plans to introduce Expand to the typing module.
You asked whether PEP 646 would be updated to cover new usage of Unpack. That's not how PEPs work. They are intended to be point-in-time proposals, not live documentation of new functionality, so it wouldn't be appropriate to update PEP 646 to cover new uses of Unpack. That will be covered in PEP 692.
Reacted by Alex Rattray, Jongwook Choi, herr kaste, Steven Seguin, adam-grant-hendry, Noah and Spencer Phillip Young
There is a new python/peps#2620 underway for this functionality. It proposes to add ** syntax and use Unpack for backward compatibility. The new PEP is under review, so if you want to comment on it, I'm sure the feedback would be welcome.
Pyright has provisional support for PEP 692 if you want to play with it.
Hurray! 🎉
I'll be sure to add some comments (would love to see dual support of Unpack for TypeVarTuple and TypedDict).
PEP 589 didn't specify Expand. The only reference to Expand is in the rejected alternatives section. There are currently no plans to introduce Expand to the typing module.
Yes, thank you for clarifying it was rejected (good catch). Thank you for the clarification!
You asked whether PEP 646 would be updated to cover new usage of Unpack. That's not how PEPs work. They are intended to be point-in-time proposals, not live documentation of new functionality, so it wouldn't be appropriate to update PEP 646 to cover new uses of Unpack. That will be covered in PEP 692.
Yes, another good catch. I wasn't aware of PEP 692, which is why I was asking. However, I've seen "update" notes added to PEPs before. For example, PEP 484 has several:
If type hinting proves useful in general, a syntax for typing variables may be provided in a future Python version. (UPDATE: This syntax was added in Python 3.6 through PEP 526.)
Could an update note be added to PEP 484 if and when PEP 692 is approved? This would help readers and add traceability between the two. Something as simple as
UPDATE: Usage of Unpack also for **kwargs and TypedDict is proposed in PEP 692.
Since kwargs values can have defaults. Unpacking with defaults would be nice to support.
Is it not possible to Unpack a pydantic(BaseModel) ?
This would make integrations with libraries like strawberry easy.
For example
class AllFilters(BaseModel):
beg_age: int = 21
end_age: int = 55
beg_height: int = 120
end_height: int = 240
search_term: str = ' '
gender: str = 'female'
@strawberry.type
class Query:
@strawberry.field
async def get_filtered_users(self, **kwargs: Unpack[AllFilters]) -> List[User]:
'''This function gets all users that match the filter criteria
Strong default values are set to prevent empty results
'''
Activity
JukkaL commentedon Jan 9, 2018
The proposed syntax is problematic, as it could also mean a homogeneous
**optionswhere each value is anOptions. We could work around this with some extra syntax, but it could look messy. Here's an idea:Expand[...]could also be used for variadic type variables in case we add support for them (for example,Callable[[int, Expand[X]], None]ifXis a variadic type variable).I don't like the idea of implicitly assuming
total=False.ilevkivskyi commentedon Jan 9, 2018
Yes, the syntax would be ambiguous. Introducing
Expandonly for this purpose may be not worth it, but using it also for variadic type variables is a good idea (and it feels natural).OK, let's then drop this. In any case it is easy to add it explicitly (I think it will be used often, since keyword arguments are often optional).
ilevkivskyi commentedon Mar 15, 2019
Raising priority to normal, since this is a very common request (and we have agreed on using
Expand[...]).rmorshea commentedon Mar 15, 2019
@ilevkivskyi @JukkaL I'm in pretty desperate need of this so I might be able to justify working on this to my employer. Do you have any ideas about how to implement this? If not, are there areas of the code-base that I should look at in order to understand how to approach the problem?
ilevkivskyi commentedon Mar 16, 2019
Although this is not super tricky feature, it may be not the best choice for the first contribution, unless you like to live dangerously :-)
You will need to add a new special form
Expand[...]tomypy_extensions(whereTypedDictlives). This should be allowed only as annotation to**kwars, and must have only one type parameter which is aTypedDict. Then look at all things that contain wordsformal_to_actualand/oractual_to_formal, they might need to be updated.Also it may be not the best time to do this, since we are in the middle of a big refactoring of semantic analyzer (one of the first steps in mypy logic, that binds names to symbols). It will take two-three more weeks to finish. Otherwise you would need to repeat some parts of logic twice: in
mypy/semanal.pyand inmypy/newsemanal/semanal.py.rmorshea commentedon Mar 18, 2019
@ilevkivskyi, hmm maybe I'll hold off then. Let me know if there's any work a new contributor could provide in order to help out.
ilevkivskyi commentedon Mar 18, 2019
@rmorshea You can try one of these: good-first-issue (unless they are too easy/not interesting).
untitaker commentedon Jun 24, 2019
I got around to hacking something together in #7051. It's obviously very rough but hopefully I can build something off of that.
BTW, I think
Expandcompletely breaks the typesystem. What does this do?You could argue that this should work because having a type that just works for
**kwargsis ugly. You could also argue that this is nonsense and should not be allowed.I think
Expandcannot be called a type. IMO the proper solution would be to introduce a breaking change in the typesystem and make**kwargs: XexpandXautomatically. Code that looks like this right now:would have to be changed to:
25 remaining items
krystean commentedon Mar 27, 2022
Ah but we can specify totality like:
So
baror any key is not required anymore. DoesUnpackstill report this as an error?I now realize it's not
Unpackthat should supportUnion, it should beTypedDict. AndTypedDictalready does.MyTypedDict | OtherTypedDictmeans either of the two, not combining them. Maybe what I meant was likeUnpack[MyTypedDict, OtherTypedDict]where it unpacks multipleTypedDicts. But I guess that would complicate things.Thank you!
dgellow commentedon Apr 6, 2022
@krystean Just to add to what you said, a small thing I found out yesterday is that you can also use
RequiredandNotRequiredforTypedDictif you want to have more control on individual keys (total=is for all keys of the typed dict).See PEP-655 (also available via
typing_extensions).rattrayalex commentedon Apr 6, 2022
I am interested in sponsoring work on support for
Unpack[T]in mypy if anyone is interested in putting up a PR for this and would find sponsorship helpful.RonnyPfannschmidt commentedon Apr 27, 2022
this should also consider partial views on a class
for example if i had a type like
and i want to declare helpers like
devmessias commentedon May 29, 2022
This works for me
Are there any prospects to bring Unpack to mypy?
devmessias commentedon May 29, 2022
Would be great to have unpack available in mypy
tony commentedon May 30, 2022
@RonnyPfannschmidt Could you make this into an independent issue for this so it gets attention?
dataclasscan be unpacked to act as a TypedDictexcludefields**kw: Magic(conformance with Allow using TypedDict for more precise typing of **kwds #4441P.S. if I understand correctly, you want to be able to take dictionary data,, and hydrate models / dataclasses via
**kw. Is that an accurate depiction of what you're getting at?RonnyPfannschmidt commentedon May 30, 2022
@tony i don't want to hydrate, i want to correctly type helper functions for querying, waiting and other actions that replicate all if not most fields of types
adam-grant-hendry commentedon Jun 13, 2022
It seems
Unpackfromtyping_extensionsdoes more than whatPEP 646originally proposed (i.e. originally only for*args/TypeVarTuple, not also**kwargs/TypedDict). It now seems to also do whatExpandwas proposed for inPEP 589. This is perfectly fine by me (I love havingUnpack), but I have some questions:ExpandinPEP 646also going to be implemented?PEP 646be updated to indicate thatUnpackmay also be used for variadicTypeDicts?I think ideally there should only be 1 name and it should work for both types. (It would be confusing, for instance, to have both
ExpandandUnpack).ASIDE:
If it is agreed there should be one name, my vote is for
Unpack, simply because it's already implemented and the terminology matches Python terminology (we speak of "packing"/"unpacking" variables, rather than "collapsing"/"expanding" variables).PEP 646appears to have implemented a grammar change to utilize starred expressions in indeces beginning in Python 3.11. Could this be expanded to also implement double-starred expressions? There've been discussions here that implementing a**syntax would require a grammar change. I'm wondering if expanding the grammar change for*would make this less work? This would be a nice-to-have (much like how the grammar was updated to support built-ins in the syntax directly rather than requiringfrom typing import UppercaseBuiltinand pipes forUnion).erictraut commentedon Jun 13, 2022
There is a new PEP 692 underway for this functionality. It proposes to add
**syntax and useUnpackfor backward compatibility. The new PEP is under review, so if you want to comment on it, I'm sure the feedback would be welcome.Pyright has provisional support for PEP 692 if you want to play with it.
A few additional notes...
PEP 589 didn't specify
Expand. The only reference toExpandis in the rejected alternatives section. There are currently no plans to introduceExpandto thetypingmodule.You asked whether PEP 646 would be updated to cover new usage of
Unpack. That's not how PEPs work. They are intended to be point-in-time proposals, not live documentation of new functionality, so it wouldn't be appropriate to update PEP 646 to cover new uses ofUnpack. That will be covered in PEP 692.adam-grant-hendry commentedon Jun 14, 2022
Hurray! 🎉
I'll be sure to add some comments (would love to see dual support of
UnpackforTypeVarTupleandTypedDict).Yes, thank you for clarifying it was rejected (good catch). Thank you for the clarification!
Yes, another good catch. I wasn't aware of
PEP 692, which is why I was asking. However, I've seen "update" notes added to PEPs before. For example, PEP 484 has several:Could an update note be added to
PEP 484if and whenPEP 692is approved? This would help readers and add traceability between the two. Something as simple asUPDATE: Usage of
Unpackalso for**kwargsandTypedDictis proposed inPEP 692.would be great.
Allow using TypedDict for more precise typing of **kwds (#13471)
Allow using TypedDict for more precise typing of **kwds (#13471)
XChikuX commentedon Jun 9, 2023
Since kwargs values can have defaults. Unpacking with defaults would be nice to support.
Is it not possible to Unpack a pydantic(BaseModel) ?
This would make integrations with libraries like strawberry easy.
For example