Skip to content

Commit d469295

Browse files
authored
Sync typeshed, fix strict equality dict view checks (#11200)
Source commit: python/typeshed@a9227ed Fixes for python/typeshed#6039 Co-authored-by: hauntsaninja <>
1 parent 3ec0284 commit d469295

File tree

9 files changed

+145
-49
lines changed

9 files changed

+145
-49
lines changed

mypy/checkexpr.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@
9494

9595
# Types considered safe for comparisons with --strict-equality due to known behaviour of __eq__.
9696
# NOTE: All these types are subtypes of AbstractSet.
97-
OVERLAPPING_TYPES_WHITELIST: Final = [
97+
OVERLAPPING_TYPES_ALLOWLIST: Final = [
9898
"builtins.set",
9999
"builtins.frozenset",
100100
"typing.KeysView",
101101
"typing.ItemsView",
102+
"builtins._dict_keys",
103+
"builtins._dict_items",
102104
]
103105

104106

@@ -2357,8 +2359,8 @@ def dangerous_comparison(self, left: Type, right: Type,
23572359
return False
23582360
if isinstance(left, Instance) and isinstance(right, Instance):
23592361
# Special case some builtin implementations of AbstractSet.
2360-
if (left.type.fullname in OVERLAPPING_TYPES_WHITELIST and
2361-
right.type.fullname in OVERLAPPING_TYPES_WHITELIST):
2362+
if (left.type.fullname in OVERLAPPING_TYPES_ALLOWLIST and
2363+
right.type.fullname in OVERLAPPING_TYPES_ALLOWLIST):
23622364
abstract_set = self.chk.lookup_typeinfo('typing.AbstractSet')
23632365
left = map_instance_to_supertype(left, abstract_set)
23642366
right = map_instance_to_supertype(right, abstract_set)

mypy/typeshed/stdlib/builtins.pyi

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ from _typeshed import (
2121
)
2222
from ast import AST, mod
2323
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
24-
from types import CodeType, TracebackType
24+
from types import CodeType, MappingProxyType, TracebackType
2525
from typing import (
2626
IO,
2727
AbstractSet,
@@ -73,6 +73,8 @@ _T_co = TypeVar("_T_co", covariant=True)
7373
_T_contra = TypeVar("_T_contra", contravariant=True)
7474
_KT = TypeVar("_KT")
7575
_VT = TypeVar("_VT")
76+
_KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers.
77+
_VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
7678
_S = TypeVar("_S")
7779
_T1 = TypeVar("_T1")
7880
_T2 = TypeVar("_T2")
@@ -787,6 +789,20 @@ class list(MutableSequence[_T], Generic[_T]):
787789
if sys.version_info >= (3, 9):
788790
def __class_getitem__(cls, item: Any) -> GenericAlias: ...
789791

792+
class _dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]):
793+
if sys.version_info >= (3, 10):
794+
mapping: MappingProxyType[_KT_co, _VT_co]
795+
796+
# The generics are the wrong way around because of a mypy limitation
797+
# https://github.com/python/mypy/issues/11138
798+
class _dict_values(ValuesView[_VT_co], Generic[_VT_co, _KT_co]):
799+
if sys.version_info >= (3, 10):
800+
mapping: MappingProxyType[_KT_co, _VT_co]
801+
802+
class _dict_items(ItemsView[_KT_co, _VT_co], Generic[_KT_co, _VT_co]):
803+
if sys.version_info >= (3, 10):
804+
mapping: MappingProxyType[_KT_co, _VT_co]
805+
790806
class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]):
791807
@overload
792808
def __init__(self: dict[_KT, _VT]) -> None: ...
@@ -796,6 +812,10 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]):
796812
def __init__(self, map: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ...
797813
@overload
798814
def __init__(self, iterable: Iterable[Tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
815+
# Next overload is for dict(string.split(sep) for string in iterable)
816+
# Cannot be Iterable[Sequence[_T]] or otherwise dict(["foo", "bar", "baz"]) is not an error
817+
@overload
818+
def __init__(self: dict[str, str], iterable: Iterable[list[str]]) -> None: ...
799819
def __new__(cls: Type[_T1], *args: Any, **kwargs: Any) -> _T1: ...
800820
def clear(self) -> None: ...
801821
def copy(self) -> dict[_KT, _VT]: ...
@@ -807,9 +827,9 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]):
807827
def update(self, __m: Iterable[Tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
808828
@overload
809829
def update(self, **kwargs: _VT) -> None: ...
810-
def keys(self) -> KeysView[_KT]: ...
811-
def values(self) -> ValuesView[_VT]: ...
812-
def items(self) -> ItemsView[_KT, _VT]: ...
830+
def keys(self) -> _dict_keys[_KT, _VT]: ...
831+
def values(self) -> _dict_values[_VT, _KT]: ...
832+
def items(self) -> _dict_items[_KT, _VT]: ...
813833
@classmethod
814834
@overload
815835
def fromkeys(cls, __iterable: Iterable[_T], __value: None = ...) -> dict[_T, Any | None]: ...

mypy/typeshed/stdlib/collections/__init__.pyi

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
11
import sys
22
from _typeshed import Self
3+
from builtins import _dict_items, _dict_keys, _dict_values
34
from typing import Any, Dict, Generic, NoReturn, Tuple, Type, TypeVar, overload
45

56
if sys.version_info >= (3, 10):
6-
from typing import (
7-
Callable,
8-
ItemsView,
9-
Iterable,
10-
Iterator,
11-
KeysView,
12-
Mapping,
13-
MutableMapping,
14-
MutableSequence,
15-
Reversible,
16-
Sequence,
17-
ValuesView,
18-
)
7+
from typing import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Reversible, Sequence
198
else:
209
from _collections_abc import *
2110

2211
_S = TypeVar("_S")
2312
_T = TypeVar("_T")
2413
_KT = TypeVar("_KT")
2514
_VT = TypeVar("_VT")
15+
_KT_co = TypeVar("_KT_co", covariant=True)
16+
_VT_co = TypeVar("_VT_co", covariant=True)
2617

2718
# namedtuple is special-cased in the type checker; the initializer is ignored.
2819
if sys.version_info >= (3, 7):
@@ -247,23 +238,25 @@ class Counter(Dict[_T, int], Generic[_T]):
247238
def __iand__(self, other: Counter[_T]) -> Counter[_T]: ...
248239
def __ior__(self, other: Counter[_T]) -> Counter[_T]: ... # type: ignore
249240

250-
class _OrderedDictKeysView(KeysView[_KT], Reversible[_KT]):
251-
def __reversed__(self) -> Iterator[_KT]: ...
241+
class _OrderedDictKeysView(_dict_keys[_KT_co, _VT_co], Reversible[_KT_co]):
242+
def __reversed__(self) -> Iterator[_KT_co]: ...
252243

253-
class _OrderedDictItemsView(ItemsView[_KT, _VT], Reversible[Tuple[_KT, _VT]]):
254-
def __reversed__(self) -> Iterator[Tuple[_KT, _VT]]: ...
244+
class _OrderedDictItemsView(_dict_items[_KT_co, _VT_co], Reversible[Tuple[_KT_co, _VT_co]]):
245+
def __reversed__(self) -> Iterator[Tuple[_KT_co, _VT_co]]: ...
255246

256-
class _OrderedDictValuesView(ValuesView[_VT], Reversible[_VT]):
257-
def __reversed__(self) -> Iterator[_VT]: ...
247+
# The generics are the wrong way around because of a mypy limitation
248+
# https://github.com/python/mypy/issues/11138
249+
class _OrderedDictValuesView(_dict_values[_VT_co, _KT_co], Reversible[_VT_co], Generic[_VT_co, _KT_co]):
250+
def __reversed__(self) -> Iterator[_VT_co]: ...
258251

259252
class OrderedDict(Dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]):
260253
def popitem(self, last: bool = ...) -> Tuple[_KT, _VT]: ...
261254
def move_to_end(self, key: _KT, last: bool = ...) -> None: ...
262255
def copy(self: _S) -> _S: ...
263256
def __reversed__(self) -> Iterator[_KT]: ...
264-
def keys(self) -> _OrderedDictKeysView[_KT]: ...
257+
def keys(self) -> _OrderedDictKeysView[_KT, _VT]: ...
265258
def items(self) -> _OrderedDictItemsView[_KT, _VT]: ...
266-
def values(self) -> _OrderedDictValuesView[_VT]: ...
259+
def values(self) -> _OrderedDictValuesView[_VT, _KT]: ...
267260

268261
class defaultdict(Dict[_KT, _VT], Generic[_KT, _VT]):
269262
default_factory: Callable[[], _VT] | None

mypy/typeshed/stdlib/importlib/abc.pyi

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import sys
22
import types
3-
from _typeshed import StrOrBytesPath
3+
from _typeshed import (
4+
OpenBinaryMode,
5+
OpenBinaryModeReading,
6+
OpenBinaryModeUpdating,
7+
OpenBinaryModeWriting,
8+
OpenTextMode,
9+
StrOrBytesPath,
10+
StrPath,
11+
)
412
from abc import ABCMeta, abstractmethod
513
from importlib.machinery import ModuleSpec
6-
from typing import IO, Any, Iterator, Mapping, Protocol, Sequence, Tuple, Union
14+
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
15+
from typing import IO, Any, BinaryIO, Iterator, Mapping, Protocol, Sequence, Tuple, Union, overload
716
from typing_extensions import Literal, runtime_checkable
817

918
_Path = Union[bytes, str]
@@ -84,22 +93,79 @@ if sys.version_info >= (3, 7):
8493
if sys.version_info >= (3, 9):
8594
@runtime_checkable
8695
class Traversable(Protocol):
96+
@abstractmethod
97+
def is_dir(self) -> bool: ...
98+
@abstractmethod
99+
def is_file(self) -> bool: ...
87100
@abstractmethod
88101
def iterdir(self) -> Iterator[Traversable]: ...
89102
@abstractmethod
90-
def read_bytes(self) -> bytes: ...
103+
def joinpath(self, child: StrPath) -> Traversable: ...
104+
# The .open method comes from pathlib.pyi and should be kept in sync.
105+
@overload
91106
@abstractmethod
92-
def read_text(self, encoding: str | None = ...) -> str: ...
107+
def open(
108+
self,
109+
mode: OpenTextMode = ...,
110+
buffering: int = ...,
111+
encoding: str | None = ...,
112+
errors: str | None = ...,
113+
newline: str | None = ...,
114+
) -> TextIOWrapper: ...
115+
# Unbuffered binary mode: returns a FileIO
116+
@overload
93117
@abstractmethod
94-
def is_dir(self) -> bool: ...
118+
def open(
119+
self, mode: OpenBinaryMode, buffering: Literal[0], encoding: None = ..., errors: None = ..., newline: None = ...
120+
) -> FileIO: ...
121+
# Buffering is on: return BufferedRandom, BufferedReader, or BufferedWriter
122+
@overload
95123
@abstractmethod
96-
def is_file(self) -> bool: ...
124+
def open(
125+
self,
126+
mode: OpenBinaryModeUpdating,
127+
buffering: Literal[-1, 1] = ...,
128+
encoding: None = ...,
129+
errors: None = ...,
130+
newline: None = ...,
131+
) -> BufferedRandom: ...
132+
@overload
97133
@abstractmethod
98-
def joinpath(self, child: _Path) -> Traversable: ...
134+
def open(
135+
self,
136+
mode: OpenBinaryModeWriting,
137+
buffering: Literal[-1, 1] = ...,
138+
encoding: None = ...,
139+
errors: None = ...,
140+
newline: None = ...,
141+
) -> BufferedWriter: ...
142+
@overload
99143
@abstractmethod
100-
def __truediv__(self, child: _Path) -> Traversable: ...
144+
def open(
145+
self,
146+
mode: OpenBinaryModeReading,
147+
buffering: Literal[-1, 1] = ...,
148+
encoding: None = ...,
149+
errors: None = ...,
150+
newline: None = ...,
151+
) -> BufferedReader: ...
152+
# Buffering cannot be determined: fall back to BinaryIO
153+
@overload
101154
@abstractmethod
102-
def open(self, mode: Literal["r", "rb"] = ..., *args: Any, **kwargs: Any) -> IO[Any]: ...
103-
@property
155+
def open(
156+
self, mode: OpenBinaryMode, buffering: int, encoding: None = ..., errors: None = ..., newline: None = ...
157+
) -> BinaryIO: ...
158+
# Fallback if mode is not specified
159+
@overload
104160
@abstractmethod
161+
def open(
162+
self, mode: str, buffering: int = ..., encoding: str | None = ..., errors: str | None = ..., newline: str | None = ...
163+
) -> IO[Any]: ...
164+
@property
105165
def name(self) -> str: ...
166+
@abstractmethod
167+
def __truediv__(self, key: StrPath) -> Traversable: ...
168+
@abstractmethod
169+
def read_bytes(self) -> bytes: ...
170+
@abstractmethod
171+
def read_text(self, encoding: str | None = ...) -> str: ...

mypy/typeshed/stdlib/inspect.pyi

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,24 @@ if sys.version_info >= (3, 10):
117117
else:
118118
def signature(obj: Callable[..., Any], *, follow_wrapped: bool = ...) -> Signature: ...
119119

120+
class _void: ...
121+
class _empty: ...
122+
120123
class Signature:
121-
def __init__(self, parameters: Sequence[Parameter] | None = ..., *, return_annotation: Any = ...) -> None: ...
122-
# TODO: can we be more specific here?
123-
empty: object
124+
def __init__(
125+
self, parameters: Sequence[Parameter] | None = ..., *, return_annotation: Any = ..., __validate_parameters__: bool = ...
126+
) -> None: ...
127+
empty: _empty
124128
@property
125129
def parameters(self) -> types.MappingProxyType[str, Parameter]: ...
126130
# TODO: can we be more specific here?
127131
@property
128132
def return_annotation(self) -> Any: ...
129133
def bind(self, *args: Any, **kwargs: Any) -> BoundArguments: ...
130134
def bind_partial(self, *args: Any, **kwargs: Any) -> BoundArguments: ...
131-
def replace(self: Self, *, parameters: Sequence[Parameter] | None = ..., return_annotation: Any = ...) -> Self: ...
135+
def replace(
136+
self: Self, *, parameters: Sequence[Parameter] | Type[_void] | None = ..., return_annotation: Any = ...
137+
) -> Self: ...
132138
if sys.version_info >= (3, 10):
133139
@classmethod
134140
def from_callable(
@@ -166,7 +172,7 @@ class _ParameterKind(enum.IntEnum):
166172

167173
class Parameter:
168174
def __init__(self, name: str, kind: _ParameterKind, *, default: Any = ..., annotation: Any = ...) -> None: ...
169-
empty: Any
175+
empty: _empty
170176
name: str
171177
default: Any
172178
annotation: Any
@@ -178,7 +184,12 @@ class Parameter:
178184
KEYWORD_ONLY: ClassVar[Literal[_ParameterKind.KEYWORD_ONLY]]
179185
VAR_KEYWORD: ClassVar[Literal[_ParameterKind.VAR_KEYWORD]]
180186
def replace(
181-
self: Self, *, name: str | None = ..., kind: _ParameterKind | None = ..., default: Any = ..., annotation: Any = ...
187+
self: Self,
188+
*,
189+
name: str | Type[_void] = ...,
190+
kind: _ParameterKind | Type[_void] = ...,
191+
default: Any = ...,
192+
annotation: Any = ...,
182193
) -> Self: ...
183194

184195
class BoundArguments:

mypy/typeshed/stdlib/locale.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ D_T_FMT: int
1111
D_FMT: int
1212
T_FMT: int
1313
T_FMT_AMPM: int
14+
AM_STR: int
15+
PM_STR: int
1416

1517
DAY_1: int
1618
DAY_2: int

mypy/typeshed/stdlib/multiprocessing/process.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class BaseProcess:
55
name: str
66
daemon: bool
77
authkey: bytes
8+
_identity: Tuple[int, ...] # undocumented
89
def __init__(
910
self,
1011
group: None = ...,

mypy/typeshed/stdlib/pathlib.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Path(PurePath):
8888
def mkdir(self, mode: int = ..., parents: bool = ..., exist_ok: bool = ...) -> None: ...
8989
# Adapted from builtins.open
9090
# Text mode: always returns a TextIOWrapper
91+
# The Traversable .open in stdlib/importlib/abc.pyi should be kept in sync with this.
9192
@overload
9293
def open(
9394
self,

test-data/unit/pythoneval.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,7 @@ def thing(stuff: StuffDict) -> int: ...
14141414
[out]
14151415
_testNewAnalyzerTypedDictInStub_newsemanal.py:2: note: Revealed type is "def (stuff: TypedDict('stub.StuffDict', {'foo': builtins.str, 'bar': builtins.int})) -> builtins.int"
14161416

1417-
[case testStrictEqualityWhitelist]
1417+
[case testStrictEqualityAllowlist]
14181418
# mypy: strict-equality
14191419
{1} == frozenset({1})
14201420
frozenset({1}) == {1}
@@ -1429,10 +1429,10 @@ frozenset({1}) == [1] # Error
14291429
{1: 2}.values() == {2} # Error
14301430
{1: 2}.keys() == [1] # Error
14311431
[out]
1432-
_testStrictEqualityWhitelist.py:5: error: Non-overlapping equality check (left operand type: "FrozenSet[int]", right operand type: "List[int]")
1433-
_testStrictEqualityWhitelist.py:11: error: Non-overlapping equality check (left operand type: "KeysView[int]", right operand type: "Set[str]")
1434-
_testStrictEqualityWhitelist.py:12: error: Non-overlapping equality check (left operand type: "ValuesView[int]", right operand type: "Set[int]")
1435-
_testStrictEqualityWhitelist.py:13: error: Non-overlapping equality check (left operand type: "KeysView[int]", right operand type: "List[int]")
1432+
_testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "FrozenSet[int]", right operand type: "List[int]")
1433+
_testStrictEqualityAllowlist.py:11: error: Non-overlapping equality check (left operand type: "_dict_keys[int, int]", right operand type: "Set[str]")
1434+
_testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "_dict_values[int, int]", right operand type: "Set[int]")
1435+
_testStrictEqualityAllowlist.py:13: error: Non-overlapping equality check (left operand type: "_dict_keys[int, int]", right operand type: "List[int]")
14361436

14371437
[case testUnreachableWithStdlibContextManagers]
14381438
# mypy: warn-unreachable, strict-optional

0 commit comments

Comments
 (0)