-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow using TypedDict for more precise typing of **kwds #4441
Comments
The proposed syntax is problematic, as it could also mean a homogeneous from mypy_extensions import Expand
...
def fun(x: int, *, **options: Expand[Options]) -> None:
...
I don't like the idea of implicitly assuming |
Yes, the syntax would be ambiguous. Introducing
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). |
Raising priority to normal, since this is a very common request (and we have agreed on using |
@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 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 |
@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. |
@rmorshea You can try one of these: good-first-issue (unless they are too easy/not interesting). |
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 x: Expand[dict[str, int]] = 42 You could argue that this should work because having a type that just works for I think def foo(**kwargs: int): ... would have to be changed to: def foo(**kwargs: Dict[str, int]): ... |
@ilevkivskyi I would like to hear your thoughts on changing the behavior on kwargs to expand automatically. The more I think about it the more I resent the |
If you mean this:
then no. Just the scale of possible breakages is already an enough reason not to do this. In addition this will introduce an inconsistency with |
Yes, I want to change I think the mypy project will have to think about a system for introducing breaking changes (such as versioning hints) because right now it digs itself into a rabbit hole by building on mistakes for the sake of preserving compat. |
Just to save time, there is very little chance you will convince all of us. By us I mean the whole static typing community, there is also Pyre, pytype, PyRight, PyCharm, etc. and type system and syntax is shared by all of us and is standardized by several PEPs. If you really want to try this, start a discussion at |
I don't really feel authorative enough to post here, but I figured there's an off chance that you'll like my suggestion: What about |
I'm happy with either Expand or **, but my current project would really benefit from this ASAP, so whatever gets the most agreement soonest is good for me. I see that the PEP 589 already mentions Expand as a proposition. |
This would be really useful to have in a project I'm working on ATM. Is there any chance someone authoritative could take a look at #4441? Or is there any other way I could help out? My vote is for |
According to microsoft/pyright#3002 (comment), this is now supported in Pylance: from typing_extensions import Unpack
class MyKwargs(TypedDict):
foo: str
bar: int
def baz(**kwargs: Unpack[MyKwargs]) -> None:
pass
baz(foo="str", bar=3) # Pylance will affirm these types. I am not sure what the current behavior of this with mypy is. |
What happens if we don't provide Does Thank you! |
If you don't provide
|
Ah but we can specify totality like:
So
I now realize it's not Thank you! |
I am interested in sponsoring work on support for |
this should also consider partial views on a class for example if i had a type like @dataclass
class MyModel:
id: uuid
name: str
hostname: str
memory_size: int and i want to declare helpers like # bad names
Magic = SubsetTypedDict(MyModel, exclude=["id"], total=False)
def wait_for_update(model_or_id: Model|uuid, **kw: Magic) -> Model:
... |
This works for me from typing import Callable, TypedDict, Union
from typing_extensions import NotRequired, TypedDict
from typing_extensions import Unpack
class MyFuncKwargs(TypedDict):
a: NotRequired[str]
b: NotRequired[int]
def pint(b: int) -> None:
print(b)
def pstr(a: str) -> None:
print(a)
def my_func(f:Union[Callable[[str], None], Callable[[int], None]], **kwargs: Unpack[MyFuncKwargs]) -> None:
print(kwargs)
return
my_func(pstr, a="a")
my_func(pint, b=2)
my_func(pint, c=2)
my_func(pint, a=2) /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" in function "my_func"
"Literal[2]" is incompatible with "str" (reportGeneralTypeIssues)
2 errors, 0 warnings, 0 informations
Completed in 0.877sec
Are there any prospects to bring Unpack to mypy? |
Would be great to have unpack available in mypy |
@RonnyPfannschmidt Could you make this into an independent issue for this so it gets attention?
P.S. if I understand correctly, you want to be able to take dictionary data,, and hydrate models / dataclasses via |
@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
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 ASIDE:
|
There is a new PEP 692 underway for this functionality. It proposes to add Pyright has provisional support for PEP 692 if you want to play with it. A few additional notes... You asked whether PEP 646 would be updated to cover new usage of |
Hurray! 🎉 I'll be sure to add some comments (would love to see dual support of
Yes, thank you for clarifying it was rejected (good catch). Thank you for the clarification!
Yes, another good catch. I wasn't aware of
Could an update note be added to UPDATE: Usage of would be great. |
Fixes #4441 This uses a different approach than the initial attempt, but I re-used some of the test cases from the older PR. The initial idea was to eagerly expand the signature of the function during semantic analysis, but it didn't work well with fine-grained mode and also mypy in general relies on function definition and its type being consistent (and rewriting `FuncDef` sounds too sketchy). So instead I add a boolean flag to `CallableType` to indicate whether type of `**kwargs` is each item type or the "packed" type. I also add few helpers and safety net in form of a `NewType()`, but in general I am surprised how few places needed normalizing the signatures (because most relevant code paths go through `check_callable_call()` and/or `is_callable_compatible()`). Currently `Unpack[...]` is hidden behind `--enable-incomplete-features`, so this will be too, but IMO this part is 99% complete (you can see even some more exotic use cases like generic TypedDicts and callback protocols in test cases).
Fixes #4441 This uses a different approach than the initial attempt, but I re-used some of the test cases from the older PR. The initial idea was to eagerly expand the signature of the function during semantic analysis, but it didn't work well with fine-grained mode and also mypy in general relies on function definition and its type being consistent (and rewriting `FuncDef` sounds too sketchy). So instead I add a boolean flag to `CallableType` to indicate whether type of `**kwargs` is each item type or the "packed" type. I also add few helpers and safety net in form of a `NewType()`, but in general I am surprised how few places needed normalizing the signatures (because most relevant code paths go through `check_callable_call()` and/or `is_callable_compatible()`). Currently `Unpack[...]` is hidden behind `--enable-incomplete-features`, so this will be too, but IMO this part is 99% complete (you can see even some more exotic use cases like generic TypedDicts and callback protocols in test cases).
Since kwargs values can have defaults. Unpacking with defaults would be nice to support. For example
|
There are some situations where a user wants to have more precisely typed
**kwds
. Current syntax only allows homogeneous**kwds
: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 withtotal=False
. Also it is worth mentioning that this feature will allow reusing theTypedDict
s in modules where several functions have same (or similar) option sets.The text was updated successfully, but these errors were encountered: