From 76698412bc1f3ca99000d52649acd5a0e06aa71d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 15 Sep 2025 13:47:02 +0100 Subject: [PATCH 1/7] Fix crash on recursive alias in indirection.py (#19845) Fixes https://github.com/python/mypy/issues/19836 Fix is trivial, I am surprised none of the existing tests caught this in the original PR. After this is merged, I am going to add the test to `master`. --- mypy/indirection.py | 5 +++-- test-data/unit/check-incremental.test | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mypy/indirection.py b/mypy/indirection.py index 06a158818fbe..8c24c7242c96 100644 --- a/mypy/indirection.py +++ b/mypy/indirection.py @@ -39,8 +39,9 @@ def find_modules(self, typs: Iterable[types.Type]) -> set[str]: def _visit(self, typ: types.Type) -> None: if isinstance(typ, types.TypeAliasType): # Avoid infinite recursion for recursive type aliases. - if typ not in self.seen_aliases: - self.seen_aliases.add(typ) + if typ in self.seen_aliases: + return + self.seen_aliases.add(typ) typ.accept(self) def _visit_type_tuple(self, typs: tuple[types.Type, ...]) -> None: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index defe7402730f..1658e56f582b 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2577,6 +2577,13 @@ C(1)[0] [builtins fixtures/list.pyi] [out] +[case testSerializeRecursiveAlias] +from typing import Callable, Union + +Node = Union[str, int, Callable[[], "Node"]] +n: Node +[out] + [case testSerializeRecursiveAliases1] from typing import Type, Callable, Union From 2f3f03c3e39e68dbfa3544c01a34f99803b3e1c2 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Tue, 16 Sep 2025 11:52:15 -0400 Subject: [PATCH 2/7] Bump version to 1.18.2+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 be15f5395fbe..6405b6595b7a 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.18.1" +__version__ = "1.18.2+dev" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 2c0510c84868b6bb42ef0f305b701e530a85c25f Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Sat, 13 Sep 2025 10:56:02 -0700 Subject: [PATCH 3/7] stubtest: additional guidance on errors when runtime is object.__init__ (#19733) Fixes #19732 This is a simple check to point users in the right direction when they get errors because their class uses `__new__` but they wrote stubs for `__init__`. I don't feel strongly about the exact wording used here. I also considered "Maybe you meant to define `__new__` instead of `__init__`?". (cherry picked from commit 530bdc5063f2309702ec08797388d635cad4b634) --- mypy/stubtest.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d4f96a3d9389..4126f3959ee1 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -1053,7 +1053,10 @@ def get_kind(arg_name: str) -> nodes.ArgKind: def _verify_signature( - stub: Signature[nodes.Argument], runtime: Signature[inspect.Parameter], function_name: str + stub: Signature[nodes.Argument], + runtime: Signature[inspect.Parameter], + function_name: str, + warn_runtime_is_object_init: bool = False, ) -> Iterator[str]: # Check positional arguments match up for stub_arg, runtime_arg in zip(stub.pos, runtime.pos): @@ -1098,6 +1101,8 @@ def _verify_signature( msg = f'runtime does not have parameter "{stub_arg.variable.name}"' if runtime.varkw is not None: msg += ". Maybe you forgot to make it keyword-only in the stub?" + elif warn_runtime_is_object_init: + msg += ". You may need to write stubs for __new__ instead of __init__." yield msg else: yield f'stub parameter "{stub_arg.variable.name}" is not keyword-only' @@ -1137,7 +1142,11 @@ def _verify_signature( if arg not in {runtime_arg.name for runtime_arg in runtime.pos[len(stub.pos) :]}: yield f'runtime parameter "{arg}" is not keyword-only' else: - yield f'runtime does not have parameter "{arg}"' + msg = f'runtime does not have parameter "{arg}"' + if warn_runtime_is_object_init: + msg += ". You may need to write stubs for __new__ instead of __init__." + yield msg + for arg in sorted(set(runtime.kwonly) - set(stub.kwonly)): if arg in {stub_arg.variable.name for stub_arg in stub.pos}: # Don't report this if we've reported it before @@ -1223,7 +1232,12 @@ def verify_funcitem( if not signature: return - for message in _verify_signature(stub_sig, runtime_sig, function_name=stub.name): + for message in _verify_signature( + stub_sig, + runtime_sig, + function_name=stub.name, + warn_runtime_is_object_init=runtime is object.__init__, + ): yield Error( object_path, "is inconsistent, " + message, @@ -1333,7 +1347,12 @@ def verify_overloadedfuncdef( stub_sig = Signature.from_overloadedfuncdef(stub) runtime_sig = Signature.from_inspect_signature(signature) - for message in _verify_signature(stub_sig, runtime_sig, function_name=stub.name): + for message in _verify_signature( + stub_sig, + runtime_sig, + function_name=stub.name, + warn_runtime_is_object_init=runtime is object.__init__, + ): # TODO: This is a little hacky, but the addition here is super useful if "has a default value of type" in message: message += ( From 9d794b57d9c5b03d61caa3286756c05e0ae3021b Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 15 Sep 2025 21:27:43 -0400 Subject: [PATCH 4/7] [mypyc] fix: inappropriate `None`s in f-strings (#19846) if a variable is Final but the value is not yet known at compile-time, and that variable is used as an input to an f-string, the f-string will incorrectly contain "None" Fixes [mypyc#1140](https://github.com/mypyc/mypyc/issues/1140) (cherry picked from commit dce8e1c407ccaa9effebbb1ed09fbf0e7070636d) --- mypyc/irbuild/specialize.py | 4 +++- mypyc/test-data/run-strings.test | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 0880c62bc7a5..576b7a7ebffd 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -719,7 +719,9 @@ def get_literal_str(expr: Expression) -> str | None: if isinstance(expr, StrExpr): return expr.value elif isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_final: - return str(expr.node.final_value) + final_value = expr.node.final_value + if final_value is not None: + return str(final_value) return None for i in range(len(exprs) - 1): diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 6960b0a04303..6a62db6ee3ee 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -412,9 +412,16 @@ def test_basics() -> None: [case testFStrings] import decimal from datetime import datetime +from typing import Final var = 'mypyc' num = 20 +final_known_at_compile_time: Final = 'hello' + +def final_value_setter() -> str: + return 'goodbye' + +final_unknown_at_compile_time: Final = final_value_setter() def test_fstring_basics() -> None: assert f'Hello {var}, this is a test' == "Hello mypyc, this is a test" @@ -451,6 +458,8 @@ def test_fstring_basics() -> None: inf_num = float('inf') assert f'{nan_num}, {inf_num}' == 'nan, inf' + assert f'{final_known_at_compile_time} {final_unknown_at_compile_time}' == 'hello goodbye' + # F-strings would be translated into ''.join[string literals, format method call, ...] in mypy AST. # Currently we are using a str.join specializer for f-string speed up. We might not cover all cases # and the rest ones should fall back to a normal str.join method call. From ca5abf09f3bfc543ac9c18a364696bc5da20bc03 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Tue, 16 Sep 2025 12:29:43 -0400 Subject: [PATCH 5/7] Typeshed cherry-pick: Make type of `unitest.mock.Any` a subclass of `Any` (#14708) --- mypy/typeshed/stdlib/unittest/mock.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index f4b59e7cab90..f3e58bcd1c00 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -508,7 +508,8 @@ class MagicProxy(Base): def create_mock(self) -> Any: ... def __get__(self, obj: Any, _type: Any | None = None) -> Any: ... -class _ANY: +# See https://github.com/python/typeshed/issues/14701 +class _ANY(Any): def __eq__(self, other: object) -> Literal[True]: ... def __ne__(self, other: object) -> Literal[False]: ... __hash__: ClassVar[None] # type: ignore[assignment] From 01a7a1285d03cb7a330359b22cb462aacb5f9720 Mon Sep 17 00:00:00 2001 From: Kevin Kannammalil Date: Thu, 18 Sep 2025 13:02:52 -0400 Subject: [PATCH 6/7] Update changelog for 1.18.2 (#19873) Changelog update for 1.18.2 Also updated the changelog to reflect the initial release being 1.18.1, since we had to bump the version due to wheels failing. This adds the cherry picked PRs mentioned in https://github.com/python/mypy/issues/19764#issuecomment-3293411266 (cherry picked from commit 4301be16747910ad00b4360dcc20152a7e377e3a) --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e6f8c2cac38..134d251d90b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ## Next Release -## Mypy 1.18 +## Mypy 1.18.1 -We’ve just uploaded mypy 1.18 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +We’ve just uploaded mypy 1.18.1 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: @@ -14,7 +14,7 @@ You can read the full documentation for this release on [Read the Docs](http://m ### Mypy Performance Improvements -Mypy 1.18 includes numerous performance improvements, resulting in about 40% speedup +Mypy 1.18.1 includes numerous performance improvements, resulting in about 40% speedup compared to 1.17 when type checking mypy itself. In extreme cases, the improvement can be 10x or higher. The list below is an overview of the various mypy optimizations. Many mypyc improvements (discussed in a separate section below) also improve performance. @@ -283,6 +283,12 @@ Related PRs: Please see [git log](https://github.com/python/typeshed/commits/main?after=2480d7e7c74493a024eaf254c5d2c6f452c80ee2+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +### Mypy 1.18.2 + +- Fix crash on recursive alias (Ivan Levkivskyi, PR [19845](https://github.com/python/mypy/pull/19845)) +- Add additional guidance for stubtest errors when runtime is `object.__init__` (Stephen Morton, PR [19733](https://github.com/python/mypy/pull/19733)) +- Fix handling of None values in f-string expressions in mypyc (BobTheBuidler, PR [19846](https://github.com/python/mypy/pull/19846)) + ### Acknowledgements Thanks to all mypy contributors who contributed to this release: From df05f05555ee62dbdb9960c64cad186172e92be1 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Thu, 18 Sep 2025 13:18:42 -0400 Subject: [PATCH 7/7] remove +dev from version --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 6405b6595b7a..fcfc04fcc40b 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.18.2+dev" +__version__ = "1.18.2" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))