Skip to content
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

Confusing error message when setting item in a heterogeneous dict #2900

Open
timabbott opened this issue Feb 24, 2017 · 10 comments
Open

Confusing error message when setting item in a heterogeneous dict #2900

timabbott opened this issue Feb 24, 2017 · 10 comments
Labels

Comments

@timabbott
Copy link

timabbott commented Feb 24, 2017

This simple piece of code throws a mypy error:

foo = dict(junk=1, bar=dict(yo=3))
foo['bar']['yo'] = 5

I've only tested on the last release (0.471)

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 24, 2017

The dictionary is heterogeneous, as it has both integer and dictionary values. In this case mypy infers foo to be a Dict[str, object]. Mypy gives the error because object does not support indexing.

The error message from mypy isn't very helpful though. For example, Dict[str, object] can only be indexed once would be a better message. Alternatively, mypy could require an annotation for foo, since it can't infer a precise type automatically. I'm leaving this issue open because the current behavior is confusing.

TypedDict is a work-in-progress mypy feature that will let you give a precise type for foo. It's still unclear if mypy will be able to automatically use a TypedDict type for foo -- it's possible that you'd still need an annotation.

@JukkaL JukkaL changed the title Bug in "Unsupported target for indexed assignment" with nested dicts Confusing error message when setting item in a heterogeneous dict Feb 24, 2017
@showell
Copy link

showell commented Feb 24, 2017

I'm not sure there's an easy way to convey this in the error message, but the mypy failure, despite the cryptic warning, led us to better code:

bar = dict(yo=3)
bar['yo'] = 5
foo = dict(junk=1, bar=bar)

Basically, we built the inner dict first before embedding it into the outer dict.

@benkuhn
Copy link
Contributor

benkuhn commented Mar 7, 2017

EDIT: I was confused and this is wrong. Original:

Isn't it a bug to infer the type as Dict[str, object] instead of Dict[str, Any] in this case? After all, the dicts contain values that are not compatible with type object. The following seems like it shouldn't issue a type error:

d = {'b': 1, 'c': 'foo'}
i = 1
i = d['b']

@gvanrossum
Copy link
Member

@benkuhn Sounds like you have the definition of compatibility backwards. Being compatible is similar to being a subclass, with some added rules regarding Any thrown in. So, since every type is a subclass of object, every type is also compatible with object.

The difference between object and Any is not that they are at opposite ends of the type hierarchy. Instead, object is at the top, but Any is not in the type hierarchy at all. The rules that distinguish the two state that Any is compatible with everything and everything is compatible with Any. (See https://www.python.org/dev/peps/pep-0483/#summary-of-gradual-typing for a longer read on the subject.)

Regardless, inferring Dict[str, Any] in this case would be sub-optimal, because it would imply that as soon as you have a value of an unintended type in your dict, mypy would shut up instead of pointing out the mistake. (After all, who says you didn't mean d to contain only string values?)

@benkuhn
Copy link
Contributor

benkuhn commented Mar 7, 2017

Oh, whoops! You're right (and I'm convinced the code example probably should issue a type error).

@OrenLeaffer
Copy link

For posterity, here's the error that I get running the latest (38453c0):

$ cat z.py
foo = dict(junk=1, bar=dict(yo=3))
foo['bar']['yo'] = 5
$ python -m mypy z.py
z.py:2: error: Unsupported target for indexed assignment

I agree that this is a confusing error.

@sid-kap
Copy link
Contributor

sid-kap commented Mar 12, 2018

@OrenLeaffer are you planning to upstream your changes in OrenLeaffer@0eedd06?

@wkschwartz
Copy link
Contributor

In the mean time, is there a canonical work-around for this problem?

@ilevkivskyi
Copy link
Member

In the mean time, is there a canonical work-around for this problem?

Probably you might use TypedDict.

@ggirelli
Copy link

ggirelli commented Feb 12, 2020

How about Dict[str, Any]?

foo: Dict[str, Any] = dict(junk=1, bar=dict(yo=3))
foo['bar']['yo'] = 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests