8000 Merge pull request #3035 from pkch/dict-literals-for-typeddict · python/mypy@6b93e63 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b93e63

Browse files
authored
Merge pull request #3035 from pkch/dict-literals-for-typeddict
Accept compatible dict in place of TypedDict
2 parents 2f6d620 + c0c9bfe commit 6b93e63

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

mypy/checkexpr.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,18 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
17091709
17101710
Translate it into a call to dict(), with provisions for **expr.
17111711
"""
1712+
# if the dict literal doesn't match TypedDict, check_typeddict_call_with_dict reports
1713+
# an error, but returns the TypedDict type that matches the literal it found
1714+
# that would cause a second error when that TypedDict type is returned upstream
1715+
# to avoid the second error, we always return TypedDict type that was requested
1716+
if isinstance(self.type_context[-1], TypedDictType):
1717+
self.check_typeddict_call_with_dict(
1718+
callee=self.type_context[-1],
1719+
kwargs=e,
1720+
context=e
1721+
)
1722+
return self.type_context[-1].copy_modified()
1723+
17121724
# Collect function arguments, watching out for **expr.
17131725
args = [] # type: List[Expression] # Regular "key: value"
17141726
stargs = [] # type: List[Expression] # For "**expr"

test-data/unit/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ Add the test in this format anywhere in the file:
2929
- `# E: abc...` indicates that this line should result in type check error
3030
with text "abc..."
3131
- note a space after `E:` and `flags:`
32-
- lines without `# E: ` should cause no type check errors
32+
- `# E:12` adds column number to the expected error
33+
- repeating `# E: ` several times in one line indicates multiple expected errors in one line
34+
- `W: ...` and `N: ...` works exactly like `E:`, but report a warning and a note respectively
35+
- lines that don't contain the above should cause no type check errors
3336
- optional `[builtins fixtures/...]` tells the type checker to use
3437
stubs from the indicated file (see Fixtures section below)
3538
- optional `[out]` is an alternative to the "# E:" notation: it indicates that

test-data/unit/check-typeddict.test

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,3 +648,42 @@ def f() -> None:
648648
A = TypedDict('A', {'x': int})
649649
A # E: Name 'A' is not defined
650650
[builtins fixtures/dict.pyi]
651+
652+
653+
-- Use dict literals
654+
655+
[case testTypedDictDictLiterals]
656+
from mypy_extensions import TypedDict
657+
658+
Point = TypedDict('Point', {'x': int, 'y': int})
659+
660+
def f(p: Point) -> None:
661+
p = {'x': 2, 'y': 3}
662+
p = {'x': 2} # E: Expected items ['x', 'y'] but found ['x'].
663+
p = dict(x=2, y=3)
664+
665+
f({'x': 1, 'y': 3})
666+
f({'x': 1, 'y': 'z'}) # E: Incompatible types (expression has type "str", TypedDict item "y" has type "int")
667+
668+
f(dict(x=1, y=3))
669+
f(dict(x=1, y=3, z=4)) # E: Expected items ['x', 'y'] but found ['x', 'y', 'z'].
670+
671+
[builtins fixtures/dict.pyi]
672+
673+
674+
[case testTypedDictExplicitTypes]
675+
from mypy_extensions import TypedDict
676+
677+
Point = TypedDict('Point', {'x': int, 'y': int})
678+
679+
p1: Point = {'x': 'hi'} # E: Expected items ['x', 'y'] but found ['x'].
680+
681+
p2: Point
682+
p2 = dict(x='bye') # E: Expected items ['x', 'y'] but found ['x'].
683+
684+
p3 = Point(x=1, y=2)
685+
p3 = {'x': 'hi'} # E: Expected items ['x', 'y'] but found ['x'].
686+
687+
p4: Point = {'x': 1, 'y': 2}
688+
689+
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)
0