-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Typed dicts with missing keys #2632
Comments
There are many reasonable use cases where some typed dict keys are only defined some of the time:
A simple approach would be have a way to annotate potentially missing keys. By default, a key would still be required. We can't use
This doesn't work:
If some key is optional, it must be accessed using
Maybe we also need to track setting optional keys in the conditional type binder:
|
When receiving JSON objects from an untrusted source, we'd want to check that it conforms to our schema before using it as a typed dict. Otherwise bad things could happen. If optional key constraints are available for introspection, we could implement a helper that checks whether a runtime dictionary object is compatible with a typed dict type. This wouldn't be part of mypy or |
FWIW hacklang conflates keys with Nullable with Optional Keys - https://docs.hhvm.com/hack/shapes/introduction#accessing-fields__nullable-fields-are-optional I wouldn't worry too much about schema validation in Mypy. Marshmallow is already great at that (And the hypothetical plugin system would allow them to play nice together 😄) |
@rowillia An approach similar to Hack sounds pretty reasonable for mypy as well. Here's how it could work:
This would clearly be unsafe, though. Maybe mypy should be able to help catch these issues. Some ideas:
I'm leaning towards (4), since it seems to provide sufficient flexibility with reasonable defaults, and it would also be safe. Also, I'd probably allow @davidfstr @gvanrossum What do you think? |
A few quick thoughts:
I don't have a lot of first-hand experience with JSON blobs that have omittable keys, so I don't have especially strong opinions on the semantics for working with them. |
Okay, here's another proposal:
I chose the name
More rationale:
Additional notes:
|
Hm, that's pretty verbose, and many people probably don't even know whether their code cares about explicit
The one use case that this doesn't cover is when you want to allow the key to be missing, but when present you don't want the value to be |
My gut feeling is that a missing key with the value never being |
Schema evolution or versioning is a typical case where it seems natural to have missing keys with non- |
OK, I withdraw my proposal. IIRC @rowillia also posted some example code that used |
I like the OptionalItem proposal, mainly on the rationale that explicit is better than implicit. |
@gvanrossum I think that this would be nice to have before we make |
@JukkaL |
@ilevkivskyi We have around 1k instances of |
@JukkaL |
We had an offline discussion about the syntax and @ddfisher wasn't happy with A = TypedDict('A', {'x': Checked[int]})
class A(TypedDict):
a: Checked[int] Pros:
Cons:
|
Hm, Checked[int] looks weird to me. I wonder if we could just have a flag
that makes all of a given TypedDict's items optional? The app should be
allowed to write td[key] if they're certain that the key exists, or
td.get(key [, default]) if they're not.
|
Having a per-TypedDict flag might be a reasonable compromise. (Note that in my current implementation Here are possible semantics in more detail:
Possible syntax: T = TypedDict('T', {'x': int}, partial=True)
def f(t: T) -> None:
t['x'] # Invalid
t.get('x') # Ok
assert 'x' in t
t['x'] # Ok
f({}) # Ok
f({'x': 2}) # Ok Other ideas for the name of the flag:
Not sure what's the best way to support the class-based syntax. We could perhaps have @partial
class T(TypedDict):
x: int |
Since the class based syntax works only in Python 3.6 anyway, I could propose another option (more similar to the functional syntax): class T(TypedDict, partial=True):
x: int |
I'm not sure I like "partial" (possible confusion with functools.partial)
but I like the class decorator. It even lets users specify a series of
required and a series of optional fields, using subclassing (only one would
use the class decorator).
|
What about 'incomplete' instead of 'partial'? Or I kind of like the idea of using the |
I like total=False, using the class keyword.
|
…#3501) Implement a general-purpose way of extending type inference of methods. Also special case TypedDict get and `int.__pow__`. Implement a new plugin system that can handle both module-level functions and methods. This an alternative to #2620 by @rowillia. I borrowed some test cases from that PR. This PR has a few major differences: * Use the plugin system instead of full special casing. * Don't support `d.get('x', {})` as it's not type safe. Once we have #2632 we can add support for this idiom safely. * Code like `f = foo.get` loses the special casing for get. Fixes #2612. Work towards #1240.
Ok I'll go with |
This was implemented in #3558. |
So in the end no way to specify which keys can be missing? It's all required or any can be missing. 😞 This seems to work…
|
@ppo Right now the way to specify some keys as required and some as optional is to have a total=False TypedDict inherit from a total=True TypedDict:
On the typing-sig mailing list there is an active discussion right now about new syntax to mark individual keys as required keys and others as optional keys. |
@davidfstr Thank you for your code. But there are still no error when I try to get an optional key. Any idea about this? class _Point2DBase(TypedDict):
x: int
y: int
class Point2D(_Point2DBase, total=False):
label: str # optional
def func(point: Point2D):
a = point["label"] # suppose to get an error but not |
@ladyrick Offhand I’m not sure why that access to an optional key is allowed. |
Indeed I tried @ladyrick 's snippet and mypy doesn't raise an error to me either. Environment:
|
We don't have a complete story for typed dicts when some keys may be missing. Support for
get
(#2612) is necessary, but it may not be sufficient. For example, consider code like this:Should it be possible to make this type check without a cast?
The text was updated successfully, but these errors were encountered: