From 652bee088fcbda2d28a018d572b5be23b9f67abe Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Sun, 6 Oct 2024 23:21:23 -0400 Subject: [PATCH 1/4] Emit error on 'raise NotImplemented' --- mypy/checker.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 4bbd49cd71984..a5a4ab727995c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4746,6 +4746,14 @@ def type_check_raise(self, e: Expression, s: RaiseStmt, optional: bool = False) # https://github.com/python/mypy/issues/11089 self.expr_checker.check_call(typ, [], [], e) + if isinstance(typ, Instance) and typ.type.fullname == "builtins._NotImplementedType": + self.fail( + message_registry.INVALID_EXCEPTION.with_additional_msg( + '; did you mean "NotImplementedError"?' + ), + s, + ) + def visit_try_stmt(self, s: TryStmt) -> None: """Type check a try statement.""" # Our enclosing frame will get the result if the try/except falls through. From 7c4a67e0e5bfbcc2ee910815c11189e9a9b5ee85 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 7 Oct 2024 10:01:45 -0400 Subject: [PATCH 2/4] Add test case for raising NotImplemented --- test-data/unit/check-statements.test | 7 +++++++ test-data/unit/fixtures/notimplemented.pyi | 10 +++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d1464423e90f0..44880cf352043 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -519,6 +519,13 @@ if object(): raise BaseException from f # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] +[case testRaiseNotImplementedFails] +if object(): + raise NotImplemented # E: Exception must be derived from BaseException; did you mean "NotImplementedError"? +if object(): + raise NotImplemented() # E: NotImplemented? not callable +[builtins fixtures/notimplemented.pyi] + [case testTryFinallyStatement] import typing try: diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index 2ca376ea07605..c13cfe0114274 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -1,6 +1,5 @@ # builtins stub used in NotImplemented related cases. -from typing import Any, cast - +from typing import Any class object: def __init__(self) -> None: pass @@ -10,5 +9,10 @@ class function: pass class bool: pass class int: pass class str: pass -NotImplemented = cast(Any, None) class dict: pass + +class _NotImplementedType(Any): + __call__: NotImplemented # type: ignore +NotImplemented = _NotImplementedType() + +class BaseException: pass From a46afa702c28426ff59740ebc1965d02ca2138b9 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 7 Oct 2024 10:27:56 -0400 Subject: [PATCH 3/4] Fix test after syncing NotImplemented fixture definition with typeshed Test case testReturnAnyForNotImplementedInNormalMethods become silently invalid after typeshed changes it defintiion of NotImplemented from Any to a subclass to Any. We can store the old behavior by special casing NotImplemented in check_return_stmt. --- mypy/checker.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index a5a4ab727995c..9021039e6e19b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4581,6 +4581,13 @@ def check_return_stmt(self, s: ReturnStmt) -> None: s.expr, return_type, allow_none_return=allow_none_func_call ) ) + # Treat NotImplemented as having type Any, consistent with its + # definition in typeshed prior to python/typeshed#4222. + if ( + isinstance(typ, Instance) + and typ.type.fullname == "builtins._NotImplementedType" + ): + typ = AnyType(TypeOfAny.special_form) if defn.is_async_generator: self.fail(message_registry.RETURN_IN_ASYNC_GENERATOR, s) From bf3a8a902f19907f4657178bd37ac3a2772601cb Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 8 Oct 2024 13:53:56 -0400 Subject: [PATCH 4/4] Tweak NotImplemented definition in fixtures/notimplemented.pyi --- test-data/unit/fixtures/notimplemented.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index c13cfe0114274..92edf84a7fd11 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -13,6 +13,6 @@ class dict: pass class _NotImplementedType(Any): __call__: NotImplemented # type: ignore -NotImplemented = _NotImplementedType() +NotImplemented: _NotImplementedType class BaseException: pass