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
Unexpected error when using get() with union of typed dicts #7151
This generates an error which could be understood as a false positive:
fromtypingimportUnionfrommypy_extensionsimportTypedDictclassA(TypedDict):
x: intclassB(TypedDict):
y: strd: Union[A, B]
d.get('x') # TypedDict "B" has no key 'x'
It was introduced by #6560. Technically this is not a false positive, since a value with type A could have a hidden y attribute with a non-str type. However, maybe we should treat this as a false positive since the behavior probably feels unexpected for most users.
This specific to union in the sense that if one of the union items contains the key, it seems reasonable to use d.get('x') to have it not fail on other union items. That is, the question is if it would be better if unions would behave differently than non-unions. We'd only generate the error if no union items contain the key.
Example:
d: Union[A, B]
d.get('x') # Maybe this should be ok, since 'x' is defined in A?d.get('y') # Ditto, since 'y' is defined in Bd.get('bad') # This should still be an error, since this could be a typo or totally bogus
I'm not convinced that this should be changed, but I think it's worth at least thinking about it. The change would clearly be quite ad hoc.
Shouldn't d.get('bad') return None? Maybe that's the bug here -- when we do d.get('x') where d is a Union[A, B], we should be inferring Union[int, None] instead of an error.
This would be a change in semantics, but it seems like a reasonable one to me? If users want the "this key doesn't exist" error, they could fall back to using indexing and do d['x']. Or maybe there's some nuance of TypedDicts that I'm missing.
I am fine with either behaviour of .get() itself, but I don't really like the idea that in some specific cases our unions should behave as unsafe unions (a.k.a. pytype unions).
There are a few reasons why d.get('bad') doesn't return None:
There could be a derived TypedDict that includes the key 'bad' with an arbitrary type. So technically a safe return type would be object.
When performing refactoring, it's useful that mypy complains about misspelled keys. For example, if you rename a key but it's used in .get(...) with a non-total TypedDict, mypy wouldn't complain about an invalid key and this could cause a bug.
If there's a typo in the key (such as writing foobar instead of fooBar), the .get(...) would silently work but produce incorrect output, and mypy wouldn't complain about it either.
Activity
ilevkivskyi commentedon Jul 4, 2019
I don't see how it is specific to unions:
JukkaL commentedon Jul 4, 2019
This specific to union in the sense that if one of the union items contains the key, it seems reasonable to use
d.get('x')to have it not fail on other union items. That is, the question is if it would be better if unions would behave differently than non-unions. We'd only generate the error if no union items contain the key.Example:
I'm not convinced that this should be changed, but I think it's worth at least thinking about it. The change would clearly be quite ad hoc.
JukkaL commentedon Jul 4, 2019
I saw actually an instance of this in the Dropbox S codebase.
Michael0x2a commentedon Jul 4, 2019
Shouldn't
d.get('bad')return None? Maybe that's the bug here -- when we dod.get('x')wheredis aUnion[A, B], we should be inferringUnion[int, None]instead of an error.This would be a change in semantics, but it seems like a reasonable one to me? If users want the "this key doesn't exist" error, they could fall back to using indexing and do
d['x']. Or maybe there's some nuance of TypedDicts that I'm missing.ilevkivskyi commentedon Jul 4, 2019
I am fine with either behaviour of
.get()itself, but I don't really like the idea that in some specific cases our unions should behave as unsafe unions (a.k.a. pytype unions).JukkaL commentedon Jul 5, 2019
There are a few reasons why
d.get('bad')doesn't return None:'bad'with an arbitrary type. So technically a safe return type would beobject..get(...)with a non-total TypedDict, mypy wouldn't complain about an invalid key and this could cause a bug.foobarinstead offooBar), the.get(...)would silently work but produce incorrect output, and mypy wouldn't complain about it either.JukkaL commentedon Jan 29, 2020
This doesn't important enough to keep open, since no consensus was reached about if (and how) this should be fixed.