Skip to content

Change TypedDict fallback to Mapping[str, object] #5933

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

Merged
merged 2 commits into from
Nov 22, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions mypy/semanal_typeddict.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,6 @@ def build_typeddict_typeinfo(self, name: str, items: List[str],
info = self.api.basic_new_typeinfo(name, fallback)
info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys,
fallback)

def patch() -> None:
# Calculate the correct value type for the fallback Mapping.
assert info.typeddict_type, "TypedDict type deleted before calling the patch"
fallback.args[1] = join.join_type_list(list(info.typeddict_type.items.values()))

# We can't calculate the complete fallback type until after semantic
# analysis, since otherwise MROs might be incomplete. Postpone a callback
# function that patches the fallback.
self.api.schedule_patch(PRIORITY_FALLBACKS, patch)
return info

# Helpers
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -4514,7 +4514,9 @@ class Bar(TypedDict):

def foo(node: NodeType) -> int:
x = node
return x['x']
# TODO: This is incorrect (https://github.com/python/mypy/issues/5930), but ensure that it
# doesn't crash at least
return x['x'] # E: Incompatible return value type (got "object", expected "int")
[builtins fixtures/isinstancelist.pyi]
[out]

Expand Down
69 changes: 25 additions & 44 deletions test-data/unit/check-typeddict.test
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Point = TypedDict('Point', {'x': int, 'y': int})
p = Point(x=42, y=1337)
reveal_type(p) # E: Revealed type is 'TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})'
# Use values() to check fallback value type.
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.int*]'
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

Expand All @@ -16,7 +16,7 @@ Point = TypedDict('Point', {'x': int, 'y': int})
p = Point(dict(x=42, y=1337))
reveal_type(p) # E: Revealed type is 'TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})'
# Use values() to check fallback value type.
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.int*]'
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

Expand All @@ -26,7 +26,7 @@ Point = TypedDict('Point', {'x': int, 'y': int})
p = Point({'x': 42, 'y': 1337})
reveal_type(p) # E: Revealed type is 'TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})'
# Use values() to check fallback value type.
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.int*]'
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

Expand All @@ -36,7 +36,7 @@ from mypy_extensions import TypedDict
EmptyDict = TypedDict('EmptyDict', {})
p = EmptyDict()
reveal_type(p) # E: Revealed type is 'TypedDict('__main__.EmptyDict', {})'
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[<nothing>]'
reveal_type(p.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

Expand Down Expand Up @@ -286,16 +286,16 @@ def widen(p: Point) -> Point3D:
from mypy_extensions import TypedDict
from typing import Mapping
Point = TypedDict('Point', {'x': int, 'y': int})
def as_mapping(p: Point) -> Mapping[str, int]:
def as_mapping(p: Point) -> Mapping[str, object]:
return p
[builtins fixtures/dict.pyi]

[case testCannotConvertTypedDictToCompatibleMapping]
[case testCannotConvertTypedDictToIncompatibleMapping]
from mypy_extensions import TypedDict
from typing import Mapping
Point = TypedDict('Point', {'x': int, 'y': int})
def as_mapping(p: Point) -> Mapping[str, str]:
return p # E: Incompatible return value type (got "Point", expected "Mapping[str, str]")
def as_mapping(p: Point) -> Mapping[str, int]:
return p # E: Incompatible return value type (got "Point", expected "Mapping[str, int]")
[builtins fixtures/dict.pyi]

[case testTypedDictAcceptsIntForFloatDuckTypes]
Expand Down Expand Up @@ -342,8 +342,8 @@ from typing import Dict, MutableMapping
Point = TypedDict('Point', {'x': int, 'y': int})
def as_dict(p: Point) -> Dict[str, int]:
return p # E: Incompatible return value type (got "Point", expected "Dict[str, int]")
def as_mutable_mapping(p: Point) -> MutableMapping[str, int]:
return p # E: Incompatible return value type (got "Point", expected "MutableMapping[str, int]")
def as_mutable_mapping(p: Point) -> MutableMapping[str, object]:
return p # E: Incompatible return value type (got "Point", expected "MutableMapping[str, object]")
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

Expand Down Expand Up @@ -377,25 +377,18 @@ f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int, 'z
from typing_extensions import Protocol
from mypy_extensions import TypedDict

class StrIntMap(Protocol):
def __getitem__(self, key: str) -> int: ...
class StrObjectMap(Protocol):
def __getitem__(self, key: str) -> object: ...

A = TypedDict('A', {'x': int, 'y': int})
B = TypedDict('B', {'x': int, 'y': str})

def fun(arg: StrIntMap) -> None: ...
def fun(arg: StrObjectMap) -> None: ...
a: A
b: B
fun(a)
fun(b) # Error
fun(b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep a test that a function that expects a protocol with __getitem__ -> int always fails.

[builtins fixtures/dict.pyi]
[out]
main:14: error: Argument 1 to "fun" has incompatible type "B"; expected "StrIntMap"
main:14: note: Following member(s) of "B" have conflicts:
main:14: note: Expected:
main:14: note: def __getitem__(self, str) -> int
main:14: note: Got:
main:14: note: def __getitem__(self, str) -> object

[case testTypedDictWithSimpleProtocolInference]
from typing_extensions import Protocol
Expand All @@ -415,7 +408,7 @@ def fun(arg: StrMap[T]) -> T:
return arg['whatever']
a: A
b: B
reveal_type(fun(a)) # E: Revealed type is 'builtins.int*'
reveal_type(fun(a)) # E: Revealed type is 'builtins.object*'
reveal_type(fun(b)) # E: Revealed type is 'builtins.object*'
[builtins fixtures/dict.pyi]
[out]
Expand All @@ -430,7 +423,7 @@ p1 = TaggedPoint(type='2d', x=0, y=0)
p2 = Point3D(x=1, y=1, z=1)
joined_points = [p1, p2][0]
reveal_type(p1.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
reveal_type(p2.values()) # E: Revealed type is 'typing.Iterable[builtins.int*]'
reveal_type(p2.values()) # E: Revealed type is 'typing.Iterable[builtins.object*]'
reveal_type(joined_points) # E: Revealed type is 'TypedDict({'x': builtins.int, 'y': builtins.int}, fallback=typing.Mapping[builtins.str, builtins.int])'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]
Expand Down Expand Up @@ -467,8 +460,8 @@ left = Cell(value=42)
right = {'score': 999} # type: Mapping[str, int]
joined1 = [left, right]
joined2 = [right, left]
reveal_type(joined1) # E: Revealed type is 'builtins.list[typing.Mapping*[builtins.str, builtins.int]]'
reveal_type(joined2) # E: Revealed type is 'builtins.list[typing.Mapping*[builtins.str, builtins.int]]'
reveal_type(joined1) # E: Revealed type is 'builtins.list[typing.Mapping*[builtins.str, builtins.object]]'
reveal_type(joined2) # E: Revealed type is 'builtins.list[typing.Mapping*[builtins.str, builtins.object]]'
[builtins fixtures/dict.pyi]

[case testJoinOfTypedDictWithCompatibleMappingSupertypeIsSupertype]
Expand All @@ -484,18 +477,6 @@ reveal_type(joined2) # E: Revealed type is 'builtins.list[typing.Sized*]'
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

[case testJoinOfTypedDictWithIncompatibleMappingIsObject]
from mypy_extensions import TypedDict
from typing import Mapping
Cell = TypedDict('Cell', {'value': int})
left = Cell(value=42)
right = {'score': 'zero'} # type: Mapping[str, str]
joined1 = [left, right]
joined2 = [right, left]
reveal_type(joined1) # E: Revealed type is 'builtins.list[builtins.object*]'
reveal_type(joined2) # E: Revealed type is 'builtins.list[builtins.object*]'
[builtins fixtures/dict.pyi]

[case testJoinOfTypedDictWithIncompatibleTypeIsObject]
from mypy_extensions import TypedDict
from typing import Mapping
Expand Down Expand Up @@ -795,13 +776,13 @@ def u(x: T, y: S) -> Union[S, T]: pass
C = TypedDict('C', {'a': int, 'b': int})

c = C(a=1, b=1)
m_s_i: Mapping[str, int]
m_s_o: Mapping[str, object]
m_s_s: Mapping[str, str]
m_i_i: Mapping[int, int]
m_s_a: Mapping[str, Any]

reveal_type(u(c, m_s_i)) # E: Revealed type is 'typing.Mapping*[builtins.str, builtins.int]'
reveal_type(u(m_s_i, c)) # E: Revealed type is 'typing.Mapping*[builtins.str, builtins.int]'
reveal_type(u(c, m_s_o)) # E: Revealed type is 'typing.Mapping*[builtins.str, builtins.object]'
reveal_type(u(m_s_o, c)) # E: Revealed type is 'typing.Mapping*[builtins.str, builtins.object]'
reveal_type(u(c, m_s_s)) # E: Revealed type is 'Union[typing.Mapping*[builtins.str, builtins.str], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]'
reveal_type(u(c, m_i_i)) # E: Revealed type is 'Union[typing.Mapping*[builtins.int, builtins.int], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]'
reveal_type(u(c, m_s_a)) # E: Revealed type is 'Union[typing.Mapping*[builtins.str, Any], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]'
Expand Down Expand Up @@ -1297,8 +1278,8 @@ class B: pass
class C(B): pass
x: X
reveal_type(x) # E: Revealed type is 'TypedDict('__main__.X', {'b': __main__.B, 'c': __main__.C})'
m1: Mapping[str, B] = x
m2: Mapping[str, C] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, C]")
m1: Mapping[str, object] = x
m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]")
[builtins fixtures/dict.pyi]

[case testForwardReferenceInClassTypedDict]
Expand All @@ -1311,8 +1292,8 @@ class B: pass
class C(B): pass
x: X
reveal_type(x) # E: Revealed type is 'TypedDict('__main__.X', {'b': __main__.B, 'c': __main__.C})'
m1: Mapping[str, B] = x
m2: Mapping[str, C] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, C]")
m1: Mapping[str, object] = x
m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]")
[builtins fixtures/dict.pyi]

[case testForwardReferenceToTypedDictInTypedDict]
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/deps-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ class I: pass
<m.P> -> m.P
<mod.I.__init__> -> m
<mod.I.__new__> -> m
<mod.I> -> <m.P>, m, m.P
<mod.I> -> <m.P>, m
<mypy_extensions.TypedDict> -> m

[case testAliasDepsTypedDictFunctional]
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -1072,8 +1072,8 @@ _testTypedDictMappingMethods.py:6: error: Revealed type is 'typing.Iterator[buil
_testTypedDictMappingMethods.py:7: error: Revealed type is 'builtins.int'
_testTypedDictMappingMethods.py:8: error: Revealed type is 'builtins.bool'
_testTypedDictMappingMethods.py:9: error: Revealed type is 'typing.AbstractSet[builtins.str*]'
_testTypedDictMappingMethods.py:10: error: Revealed type is 'typing.AbstractSet[Tuple[builtins.str*, builtins.int*]]'
_testTypedDictMappingMethods.py:11: error: Revealed type is 'typing.ValuesView[builtins.int*]'
_testTypedDictMappingMethods.py:10: error: Revealed type is 'typing.AbstractSet[Tuple[builtins.str*, builtins.object*]]'
_testTypedDictMappingMethods.py:11: error: Revealed type is 'typing.ValuesView[builtins.object*]'

[case testCrashOnComplexCheckWithNamedTupleNext]
from typing import NamedTuple
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/semanal-typeddict.test
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ MypyFile:1(
ClassDef:2(
A
BaseType(
typing.Mapping[builtins.str, builtins.str])
typing.Mapping[builtins.str, builtins.object])
ExpressionStmt:3(
StrExpr(foo))
AssignmentStmt:4(
Expand Down