From b9fa8eeaebfda2b6ac01a2651659d3206fab9854 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 30 Dec 2024 14:06:58 +0000 Subject: [PATCH 1/5] Update version to 1.14.1+dev for point release --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index eca889c708e0..6b2dae6991b4 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.14.0" +__version__ = "1.14.1+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 3755abbb875c80698c928711b5aa8f59e71ce973 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 28 Dec 2024 14:47:58 -0800 Subject: [PATCH 2/5] Fix getargs argument passing (#18350) Fixes https://github.com/mypyc/mypyc/issues/1078 Introduced in https://github.com/python/mypy/pull/17930 See the first commit to see the bug (wrong condition) --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> --- mypyc/lib-rt/getargs.c | 21 +++++++++------------ mypyc/test-data/run-classes.test | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 4f2f8aa0be83..163b9ac2b163 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -250,13 +250,12 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); } else if (nkwargs && i >= pos) { - int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); - if (res == 1) { - --nkwargs; - } - else if (res == -1) { + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { return 0; } + if (current_arg) { + --nkwargs; + } } else { current_arg = NULL; @@ -371,11 +370,12 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, Py_ssize_t j; /* make sure there are no arguments given by name and position */ for (i = pos; i < bound_pos_args && i < len; i++) { - int res = PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg); - if (res == 1) { - Py_DECREF(current_arg); + PyObject *current_arg; + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { + goto latefail; } - else if (unlikely(res == 0)) { + if (unlikely(current_arg != NULL)) { + Py_DECREF(current_arg); /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%s') " @@ -385,9 +385,6 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, kwlist[i], i+1); goto latefail; } - else if (unlikely(res == -1)) { - goto latefail; - } } /* make sure there are no extraneous keyword arguments */ j = 0; diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index cf30bddbef64..387f91e489ee 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -467,6 +467,21 @@ a = A(10) assert a.foo() == 11 assert foo() == 21 +[case testClassKwargs] +class X: + def __init__(self, msg: str, **variables: int) -> None: + pass +[file driver.py] +import traceback +from native import X +X('hello', a=0) +try: + X('hello', msg='hello') +except TypeError as e: + print(f"{type(e).__name__}: {e}") +[out] +TypeError: argument for __init__() given by name ('msg') and position (1) + [case testGenericClass] from typing import TypeVar, Generic, Sequence T = TypeVar('T') From 67f673a79d3114b3aa06d9642b9feefc2f20631a Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 30 Dec 2024 05:53:27 -0800 Subject: [PATCH 3/5] Fix enum truthiness for StrEnum (#18379) Fixes #18376 See also https://snarky.ca/unravelling-not-in-python/ --- mypy/typeops.py | 12 ++--- test-data/unit/check-enum.test | 93 ++++++++++++++++++++++++++++---- test-data/unit/fixtures/enum.pyi | 4 +- 3 files changed, 91 insertions(+), 18 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index f190168a18d7..1ffb8207ec0d 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -647,9 +647,7 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ return items -def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: - t = get_proper_type(t) - +def _get_type_method_ret_type(t: ProperType, *, name: str) -> Type | None: # For Enum literals the ret_type can change based on the Enum # we need to check the type of the enum rather than the literal if isinstance(t, LiteralType) and t.is_enum_literal(): @@ -657,9 +655,6 @@ def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: if isinstance(t, Instance): sym = t.type.get(name) - # Fallback to the metaclass for the lookup when necessary - if not sym and (m := t.type.metaclass_type): - sym = m.type.get(name) if sym: sym_type = get_proper_type(sym.type) if isinstance(sym_type, CallableType): @@ -732,7 +727,10 @@ def false_only(t: Type) -> ProperType: if ret_type: if not ret_type.can_be_false: return UninhabitedType(line=t.line) - elif isinstance(t, Instance) and t.type.is_final: + elif isinstance(t, Instance): + if t.type.is_final or t.type.is_enum: + return UninhabitedType(line=t.line) + elif isinstance(t, LiteralType) and t.is_enum_literal(): return UninhabitedType(line=t.line) new_t = copy_type(t) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index e6e42d805052..593cf43dc3a9 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -181,27 +181,100 @@ def infer_truth(truth: Truth) -> None: [case testEnumTruthyness] # mypy: warn-unreachable import enum +from typing_extensions import Literal + class E(enum.Enum): - x = 0 -if not E.x: - "noop" + zero = 0 + one = 1 + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") +if not E.zero: + print("zero is false") # E: Statement is unreachable + +if E.one: + print("one is true") +if not E.one: + print("one is false") # E: Statement is unreachable + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") + if not zero: + print("zero is false") # E: Statement is unreachable + if one: + print("one is true") + if not one: + print("one is false") # E: Statement is unreachable [builtins fixtures/tuple.pyi] -[out] -main:6: error: Statement is unreachable [case testEnumTruthynessCustomDunderBool] # mypy: warn-unreachable import enum from typing_extensions import Literal + class E(enum.Enum): - x = 0 + zero = 0 + one = 1 def __bool__(self) -> Literal[False]: return False -if E.x: - "noop" + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") # E: Statement is unreachable +if not E.zero: + print("zero is false") + +if E.one: + print("one is true") # E: Statement is unreachable +if not E.one: + print("one is false") + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") # E: Statement is unreachable + if not zero: + print("zero is false") + if one: + print("one is true") # E: Statement is unreachable + if not one: + print("one is false") +[builtins fixtures/enum.pyi] + +[case testEnumTruthynessStrEnum] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal + +class E(enum.StrEnum): + empty = "" + not_empty = "asdf" + +def print(s: str) -> None: ... + +if E.empty: + print("empty is true") +if not E.empty: + print("empty is false") + +if E.not_empty: + print("not_empty is true") +if not E.not_empty: + print("not_empty is false") + +def main(empty: Literal[E.empty], not_empty: Literal[E.not_empty]) -> None: + if empty: + print("empty is true") + if not empty: + print("empty is false") + if not_empty: + print("not_empty is true") + if not not_empty: + print("not_empty is false") [builtins fixtures/enum.pyi] -[out] -main:9: error: Statement is unreachable [case testEnumUnique] import enum diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi index debffacf8f32..135e9cd16e7c 100644 --- a/test-data/unit/fixtures/enum.pyi +++ b/test-data/unit/fixtures/enum.pyi @@ -11,6 +11,8 @@ class tuple(Generic[T]): def __getitem__(self, x: int) -> T: pass class int: pass -class str: pass +class str: + def __len__(self) -> int: pass + class dict: pass class ellipsis: pass From 667e5f752aaa4d1c62341d27af54e9ffff82620c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:00:39 +0100 Subject: [PATCH 4/5] Revert "Remove redundant inheritances from Iterator in builtins" (#18324) Revert https://github.com/python/typeshed/pull/12851 Ref: https://github.com/python/mypy/issues/18320 --- ...redundant-inheritances-from-Iterator.patch | 324 ++++++++++++++++++ mypy/typeshed/stdlib/_asyncio.pyi | 4 +- mypy/typeshed/stdlib/builtins.pyi | 10 +- mypy/typeshed/stdlib/csv.pyi | 4 +- mypy/typeshed/stdlib/fileinput.pyi | 6 +- mypy/typeshed/stdlib/itertools.pyi | 38 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- test-data/unit/pythoneval.test | 10 + 9 files changed, 368 insertions(+), 34 deletions(-) create mode 100644 misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch new file mode 100644 index 000000000000..b23461b447a1 --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch @@ -0,0 +1,324 @@ +From 25250cbe1f7ee0e924ac03b3f19297e1885dd13e Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sat, 21 Dec 2024 22:36:38 +0100 +Subject: [PATCH] Revert Remove redundant inheritances from Iterator in + builtins + +--- + mypy/typeshed/stdlib/_asyncio.pyi | 4 +- + mypy/typeshed/stdlib/builtins.pyi | 10 ++--- + mypy/typeshed/stdlib/csv.pyi | 4 +- + mypy/typeshed/stdlib/fileinput.pyi | 6 +-- + mypy/typeshed/stdlib/itertools.pyi | 38 +++++++++---------- + mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- + mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- + 7 files changed, 34 insertions(+), 34 deletions(-) + +diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi +index a25902661..18920cd8a 100644 +--- a/mypy/typeshed/stdlib/_asyncio.pyi ++++ b/mypy/typeshed/stdlib/_asyncio.pyi +@@ -1,6 +1,6 @@ + import sys + from asyncio.events import AbstractEventLoop +-from collections.abc import Awaitable, Callable, Coroutine, Generator ++from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable + from contextvars import Context + from types import FrameType + from typing import Any, Literal, TextIO, TypeVar +@@ -13,7 +13,7 @@ _T = TypeVar("_T") + _T_co = TypeVar("_T_co", covariant=True) + _TaskYieldType: TypeAlias = Future[object] | None + +-class Future(Awaitable[_T]): ++class Future(Awaitable[_T], Iterable[_T]): + _state: str + @property + def _exception(self) -> BaseException | None: ... +diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi +index 5c6d321f7..56a5969d1 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class enumerate(Generic[_T]): ++class enumerate(Iterator[tuple[int, _T]]): + def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[int, _T]: ... +@@ -1324,7 +1324,7 @@ else: + + exit: _sitebuiltins.Quitter + +-class filter(Generic[_T]): ++class filter(Iterator[_T]): + @overload + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... + @overload +@@ -1389,7 +1389,7 @@ license: _sitebuiltins._Printer + + def locals() -> dict[str, Any]: ... + +-class map(Generic[_S]): ++class map(Iterator[_S]): + @overload + def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... + @overload +@@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex + + quit: _sitebuiltins.Quitter + +-class reversed(Generic[_T]): ++class reversed(Iterator[_T]): + @overload + def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] + @overload +@@ -1693,7 +1693,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... + @overload + def vars(object: Any = ..., /) -> dict[str, Any]: ... + +-class zip(Generic[_T_co]): ++class zip(Iterator[_T_co]): + if sys.version_info >= (3, 10): + @overload + def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... +diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi +index 4a82de638..ef93129d6 100644 +--- a/mypy/typeshed/stdlib/csv.pyi ++++ b/mypy/typeshed/stdlib/csv.pyi +@@ -25,7 +25,7 @@ else: + from _csv import _reader as Reader, _writer as Writer + + from _typeshed import SupportsWrite +-from collections.abc import Collection, Iterable, Mapping, Sequence ++from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence + from typing import Any, Generic, Literal, TypeVar, overload + from typing_extensions import Self + +@@ -75,7 +75,7 @@ class excel(Dialect): ... + class excel_tab(excel): ... + class unix_dialect(Dialect): ... + +-class DictReader(Generic[_T]): ++class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): + fieldnames: Sequence[_T] | None + restkey: _T | None + restval: str | Any | None +diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi +index bf6daad0a..1e6aa78e2 100644 +--- a/mypy/typeshed/stdlib/fileinput.pyi ++++ b/mypy/typeshed/stdlib/fileinput.pyi +@@ -1,8 +1,8 @@ + import sys + from _typeshed import AnyStr_co, StrOrBytesPath +-from collections.abc import Callable, Iterable ++from collections.abc import Callable, Iterable, Iterator + from types import TracebackType +-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload ++from typing import IO, Any, AnyStr, Literal, Protocol, overload + from typing_extensions import Self, TypeAlias + + if sys.version_info >= (3, 9): +@@ -107,7 +107,7 @@ def fileno() -> int: ... + def isfirstline() -> bool: ... + def isstdin() -> bool: ... + +-class FileInput(Generic[AnyStr]): ++class FileInput(Iterator[AnyStr]): + if sys.version_info >= (3, 10): + # encoding and errors are added + @overload +diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi +index 013c3cba1..f69665882 100644 +--- a/mypy/typeshed/stdlib/itertools.pyi ++++ b/mypy/typeshed/stdlib/itertools.pyi +@@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] + + # Technically count can take anything that implements a number protocol and has an add method + # but we can't enforce the add method +-class count(Generic[_N]): ++class count(Iterator[_N]): + @overload + def __new__(cls) -> count[int]: ... + @overload +@@ -39,12 +39,12 @@ class count(Generic[_N]): + def __next__(self) -> _N: ... + def __iter__(self) -> Self: ... + +-class cycle(Generic[_T]): ++class cycle(Iterator[_T]): + def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... + +-class repeat(Generic[_T]): ++class repeat(Iterator[_T]): + @overload + def __init__(self, object: _T) -> None: ... + @overload +@@ -53,7 +53,7 @@ class repeat(Generic[_T]): + def __iter__(self) -> Self: ... + def __length_hint__(self) -> int: ... + +-class accumulate(Generic[_T]): ++class accumulate(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + @overload +@@ -61,7 +61,7 @@ class accumulate(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class chain(Generic[_T]): ++class chain(Iterator[_T]): + def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... +@@ -71,22 +71,22 @@ class chain(Generic[_T]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class compress(Generic[_T]): ++class compress(Iterator[_T]): + def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class dropwhile(Generic[_T]): ++class dropwhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class filterfalse(Generic[_T]): ++class filterfalse(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class groupby(Generic[_T_co, _S_co]): ++class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): + @overload + def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... + @overload +@@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... + +-class islice(Generic[_T]): ++class islice(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + @overload +@@ -102,19 +102,19 @@ class islice(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class starmap(Generic[_T_co]): ++class starmap(Iterator[_T_co]): + def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class takewhile(Generic[_T]): ++class takewhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... + +-class zip_longest(Generic[_T_co]): ++class zip_longest(Iterator[_T_co]): + # one iterable (fillvalue doesn't matter) + @overload + def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... +@@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class product(Generic[_T_co]): ++class product(Iterator[_T_co]): + @overload + def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... + @overload +@@ -277,7 +277,7 @@ class product(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class permutations(Generic[_T_co]): ++class permutations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... + @overload +@@ -291,7 +291,7 @@ class permutations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations(Generic[_T_co]): ++class combinations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... + @overload +@@ -305,7 +305,7 @@ class combinations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations_with_replacement(Generic[_T_co]): ++class combinations_with_replacement(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... + @overload +@@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 10): +- class pairwise(Generic[_T_co]): ++ class pairwise(Iterator[_T_co]): + def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 12): +- class batched(Generic[_T_co]): ++ class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): + if sys.version_info >= (3, 13): + def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... + else: +diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +index 61d6d0781..950ed1d8c 100644 +--- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi ++++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +@@ -1,5 +1,5 @@ + import sys +-from collections.abc import Callable, Iterable, Mapping ++from collections.abc import Callable, Iterable, Iterator, Mapping + from types import TracebackType + from typing import Any, Final, Generic, TypeVar + from typing_extensions import Self +@@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): + error_callback: Callable[[BaseException], object] | None, + ) -> None: ... + +-class IMapIterator(Generic[_T]): ++class IMapIterator(Iterator[_T]): + def __init__(self, pool: Pool) -> None: ... + def __iter__(self) -> Self: ... + def next(self, timeout: float | None = None) -> _T: ... +diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +index bc0ff6469..730404bde 100644 +--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi ++++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +@@ -397,7 +397,7 @@ class Connection: + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / + ) -> Literal[False]: ... + +-class Cursor: ++class Cursor(Iterator[Any]): + arraysize: int + @property + def connection(self) -> Connection: ... +-- +2.47.1 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi index a259026615aa..18920cd8a8a4 100644 --- a/mypy/typeshed/stdlib/_asyncio.pyi +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -1,6 +1,6 @@ import sys from asyncio.events import AbstractEventLoop -from collections.abc import Awaitable, Callable, Coroutine, Generator +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable from contextvars import Context from types import FrameType from typing import Any, Literal, TextIO, TypeVar @@ -13,7 +13,7 @@ _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _TaskYieldType: TypeAlias = Future[object] | None -class Future(Awaitable[_T]): +class Future(Awaitable[_T], Iterable[_T]): _state: str @property def _exception(self) -> BaseException | None: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 1a4ca925168a..8eb4a0a55d3c 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class enumerate(Generic[_T]): +class enumerate(Iterator[tuple[int, _T]]): def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... @@ -1322,7 +1322,7 @@ else: def exit(code: sys._ExitCode = None) -> NoReturn: ... -class filter(Generic[_T]): +class filter(Iterator[_T]): @overload def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... @overload @@ -1383,7 +1383,7 @@ def len(obj: Sized, /) -> int: ... def license() -> None: ... def locals() -> dict[str, Any]: ... -class map(Generic[_S]): +class map(Iterator[_S]): @overload def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... @overload @@ -1625,7 +1625,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... def quit(code: sys._ExitCode = None) -> NoReturn: ... -class reversed(Generic[_T]): +class reversed(Iterator[_T]): @overload def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] @overload @@ -1686,7 +1686,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... -class zip(Generic[_T_co]): +class zip(Iterator[_T_co]): if sys.version_info >= (3, 10): @overload def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 4a82de638136..ef93129d6546 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -25,7 +25,7 @@ else: from _csv import _reader as Reader, _writer as Writer from _typeshed import SupportsWrite -from collections.abc import Collection, Iterable, Mapping, Sequence +from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence from typing import Any, Generic, Literal, TypeVar, overload from typing_extensions import Self @@ -75,7 +75,7 @@ class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... -class DictReader(Generic[_T]): +class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index bf6daad0aea7..1e6aa78e2607 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -1,8 +1,8 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath -from collections.abc import Callable, Iterable +from collections.abc import Callable, Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload +from typing import IO, Any, AnyStr, Literal, Protocol, overload from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -107,7 +107,7 @@ def fileno() -> int: ... def isfirstline() -> bool: ... def isstdin() -> bool: ... -class FileInput(Generic[AnyStr]): +class FileInput(Iterator[AnyStr]): if sys.version_info >= (3, 10): # encoding and errors are added @overload diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 013c3cba120f..f69665882498 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] # Technically count can take anything that implements a number protocol and has an add method # but we can't enforce the add method -class count(Generic[_N]): +class count(Iterator[_N]): @overload def __new__(cls) -> count[int]: ... @overload @@ -39,12 +39,12 @@ class count(Generic[_N]): def __next__(self) -> _N: ... def __iter__(self) -> Self: ... -class cycle(Generic[_T]): +class cycle(Iterator[_T]): def __init__(self, iterable: Iterable[_T], /) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... -class repeat(Generic[_T]): +class repeat(Iterator[_T]): @overload def __init__(self, object: _T) -> None: ... @overload @@ -53,7 +53,7 @@ class repeat(Generic[_T]): def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... -class accumulate(Generic[_T]): +class accumulate(Iterator[_T]): @overload def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... @overload @@ -61,7 +61,7 @@ class accumulate(Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class chain(Generic[_T]): +class chain(Iterator[_T]): def __init__(self, *iterables: Iterable[_T]) -> None: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @@ -71,22 +71,22 @@ class chain(Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class compress(Generic[_T]): +class compress(Iterator[_T]): def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class dropwhile(Generic[_T]): +class dropwhile(Iterator[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class filterfalse(Generic[_T]): +class filterfalse(Iterator[_T]): def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class groupby(Generic[_T_co, _S_co]): +class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): @overload def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... @overload @@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... -class islice(Generic[_T]): +class islice(Iterator[_T]): @overload def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... @overload @@ -102,19 +102,19 @@ class islice(Generic[_T]): def __iter__(self) -> Self: ... def __next__(self) -> _T: ... -class starmap(Generic[_T_co]): +class starmap(Iterator[_T_co]): def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class takewhile(Generic[_T]): +class takewhile(Iterator[_T]): def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... -class zip_longest(Generic[_T_co]): +class zip_longest(Iterator[_T_co]): # one iterable (fillvalue doesn't matter) @overload def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... @@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class product(Generic[_T_co]): +class product(Iterator[_T_co]): @overload def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... @overload @@ -277,7 +277,7 @@ class product(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class permutations(Generic[_T_co]): +class permutations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... @overload @@ -291,7 +291,7 @@ class permutations(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations(Generic[_T_co]): +class combinations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... @overload @@ -305,7 +305,7 @@ class combinations(Generic[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... -class combinations_with_replacement(Generic[_T_co]): +class combinations_with_replacement(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... @overload @@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]): def __next__(self) -> _T_co: ... if sys.version_info >= (3, 10): - class pairwise(Generic[_T_co]): + class pairwise(Iterator[_T_co]): def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... if sys.version_info >= (3, 12): - class batched(Generic[_T_co]): + class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): if sys.version_info >= (3, 13): def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... else: diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 61d6d0781213..950ed1d8c56b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,5 +1,5 @@ import sys -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType from typing import Any, Final, Generic, TypeVar from typing_extensions import Self @@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): error_callback: Callable[[BaseException], object] | None, ) -> None: ... -class IMapIterator(Generic[_T]): +class IMapIterator(Iterator[_T]): def __init__(self, pool: Pool) -> None: ... def __iter__(self) -> Self: ... def next(self, timeout: float | None = None) -> _T: ... diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index bc0ff6469d5e..730404bde218 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -397,7 +397,7 @@ class Connection: self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / ) -> Literal[False]: ... -class Cursor: +class Cursor(Iterator[Any]): arraysize: int @property def connection(self) -> Connection: ... diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 70003545754c..397ccbb0d3f8 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -2181,3 +2181,13 @@ class Status(Enum): def imperfect(status: Status) -> str: return status.name.lower() + +[case testUnpackIteratorBuiltins] +# Regression test for https://github.com/python/mypy/issues/18320 +# Caused by https://github.com/python/typeshed/pull/12851 +x = [1, 2] +reveal_type([*reversed(x)]) +reveal_type([*map(str, x)]) +[out] +_testUnpackIteratorBuiltins.py:4: note: Revealed type is "builtins.list[builtins.int]" +_testUnpackIteratorBuiltins.py:5: note: Revealed type is "builtins.list[builtins.str]" From 251d12fe9f89f8d4818fd6ff1c48224e77119004 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Mon, 30 Dec 2024 14:24:46 +0000 Subject: [PATCH 5/5] Remove +dev from version for release 1.14.1 --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 6b2dae6991b4..dd02c98bb5c4 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.14.1+dev" +__version__ = "1.14.1" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))