pytype icon indicating copy to clipboard operation
pytype copied to clipboard

Imported TypedDict class with 'total = False' requires all fields in variable construction

Open theahura opened this issue 3 years ago • 6 comments

If I have two files, like so: test.py

from typing import TypedDict
import test2

class Foo(TypedDict, total=False):
  x: int
  y: int

x: Foo = {'x': 1}
x: test2.Foo = {'x': 1}

test2.py

from typing import TypedDict

class Foo(TypedDict, total=False):
  x: int
  y: int

I get the following error:

(env) soot@soot:~/code/soot/experimental/amol/pytype-test-partial$ pytype test.py 
Computing dependencies
Analyzing 1 sources with 1 local dependencies
ninja: Entering directory `.pytype`
[1/1] check test
FAILED: /home/soot/code/soot/experimental/amol/pytype-test-partial/.pytype/pyi/test.pyi 
/home/soot/code/soot/experimental/amol/pytype-test-partial/env/bin/python3 -m pytype.single --imports_info /home/soot/code/soot/experimental/amol/pytype-test-partial/.pytype/imports/test.imports --module-name test --platform linux -V 3.8 -o /home/soot/code/soot/experimental/amol/pytype-test-partial/.pytype/pyi/test.pyi --analyze-annotated --enable-typed-dicts --nofail --quick /home/soot/code/soot/experimental/amol/pytype-test-partial/test.py
File "/home/soot/code/soot/experimental/amol/pytype-test-partial/test.py", line 11, in <module>: Type annotation for x does not match type of assignment [annotation-type-mismatch]
  Annotation: test2.Foo(TypedDict)
  Assignment: Dict[str, int]
  
  TypedDict missing keys: y

For more details, see https://google.github.io/pytype/errors.html#annotation-type-mismatch

In other words, when I import a typeddict class and use it for a variable annotation, I have to provide all fields (even though total = False). That requirement is not enforced if the typeddict class is located in the same file.

Tested on 2022.04.26

theahura avatar Apr 27 '22 17:04 theahura

Well, that's slightly embarrassing! We never resolved this TODO to actually use the total keyword: https://github.com/google/pytype/blob/6afed8efc508c8d5a540efa48c5e435071f3cee8/pytype/pyi/classdef.py#L57.

rchen152 avatar May 01 '22 20:05 rchen152

This is a little tricky because pytd.Class doesn't have anywhere to store keywords passed to the classdef: https://github.com/google/pytype/blob/0537dc266d09a7fcc7da069238e173addb55cdfe/pytype/pytd/pytd.py#L157. What we probably want to do is change the metaclass attribute to a keywords attribute that can store things like {'metaclass': Foo, 'total': True}, but that requires some fiddly release managing to avoid pickle-related breakages.

rchen152 avatar May 01 '22 21:05 rchen152

hey there! Any progress on this one? I've been able to get around this limitation using casting for now, but just ran into a bug where I was casting incorrectly :sweat_smile: would love to remove those casts

theahura avatar May 22 '22 15:05 theahura

Just checking on this one again, somehow I'd thought this was resolved so was confused when I ran into this in a different place :sweat_smile:

theahura avatar Jul 21 '22 06:07 theahura

Sorry, haven't had time to look into this yet. I'm currently dealing with some 3.10 issues, but I should be able to work through a bit of our GitHub backlog after that.

rchen152 avatar Jul 21 '22 19:07 rchen152

hihi, ran into a somewhat related with total=False. As per the above issue, the following snippet works correctly:

from typing_extensions import TypedDict

class TestDict(TypedDict, total=False):
    a: int
    b: str

x: TestDict = {"a": 1, "b": "2"}
y: TestDict = {"a": 1}
> Success: no errors found

However, replacing the class definition with the functional definition:

from typing_extensions import TypedDict

TestDict = TypedDict("TestDict", {"a": int, "b": str}, total=False)

x: TestDict = {"a": 1, "b": "2"}
y: TestDict = {"a": 1}

incurs:

$ pytype main.py
Computing dependencies
Analyzing 1 sources with 0 local dependencies
ninja: Entering directory `.pytype'
[1/1] check main
FAILED: /home/yixun/code/py/pyProject1/.pytype/pyi/main.pyi 
/home/yixun/code/py/pyProject1/venv/bin/python -m pytype.single --imports_info /home/yixun/code/py/pyProject1/.pytype/imports/main.imports --module-name main --platform linux -V 3.7 -o /home/yixun/code/py/pyProject1/.pytype/pyi/main.pyi --analyze-annotated --nofail --quick /home/yixun/code/py/pyProject1/main.py
File "/home/yixun/code/py/pyProject1/main.py", line 6, in <module>: Type annotation for y does not match type of assignment [annotation-type-mismatch]
  Annotation: TestDict(TypedDict)
  Assignment: Dict[str, int]
  
  TypedDict missing keys: b

Using pytype 2022.08.30 on python 3.7.6

thanks for taking a look!

yixun-alpha avatar Sep 05 '22 07:09 yixun-alpha

Hey hey, just checking in on the status of this one @rchen152 Also, will fixes only be deployed to py 3.10? or is this something that we can expect to be backwards compatible?

theahura avatar Mar 03 '23 02:03 theahura