pytype
pytype copied to clipboard
Inferred type info is lost when constructing tuple
I'm not sure the title of this issue is very good. I had some trouble deciding what to call it. But hopefully this small example will make the issue more clear:
from typing import Optional, Dict, Tuple, Union, Any
def foo(d: Dict[Union[str, Tuple[str, str]], Any],
opt_k: Optional[str],
k: str):
reveal_type(opt_k) # reveals Optional[str]
if opt_k is None:
d[k] = object()
reveal_type(opt_k) # reveals None
return
reveal_type(opt_k) # reveals str
reveal_type((opt_k, k)) # reveals Tuple[Optional[str], str] (too broad)
d[(opt_k, k)] = object() # container-types-mismatch (false positive)
Here is the output from pytype:
File "/home/dominickpastore/test.py", line 6, in foo: Optional[str] [reveal-type]
File "/home/dominickpastore/test.py", line 9, in foo: None [reveal-type]
File "/home/dominickpastore/test.py", line 11, in foo: str [reveal-type]
File "/home/dominickpastore/test.py", line 12, in foo: Tuple[Optional[str], str] [reveal-type]
File "/home/dominickpastore/test.py", line 13, in foo: New container type for d does not match type annotation [container-type-mismatch]
Container: Dict[_K, _V]
Allowed contained types (from annotation Dict[Union[str, Tuple[str, str]], Any]):
_K: Union[Tuple[str, str], str]
New contained types:
_K: Tuple[Optional[str], str]
As we can see, it's correctly inferring that opt_k must not be None after the if block. Yet, if we make a tuple out of it, it seems to forget that it knows that. And if we try to use that tuple as a key in the dictionary, we get a false positive.
It seems to be a bit more complex than that, though. If we were to try to .append the tuple to a list instead, like this, the tuple's type is still revealed as Tuple[Optional[str], str], but there's no false positive:
from typing import Optional, List, Tuple, Union
def foo(l: List[Union[str, Tuple[str, str]]],
opt_k: Optional[str],
k: str):
reveal_type(opt_k) # reveals Optional[str]
if opt_k is None:
l.append(k)
reveal_type(opt_k) # reveals None
return
reveal_type(opt_k) # reveals str
reveal_type((opt_k, k)) # reveals Tuple[Optional[str], str] (too broad)
l.append((opt_k, k)) # No false positive