From a410aac5e865561ba3b39c89b60dd1f77dd6eac9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 31 Dec 2022 11:29:33 -0800 Subject: [PATCH 1/4] Add @typing_extensions.deprecated See PEP 702, python/peps#2942. This should not be merged until the PEP is merged into the repo. --- README.md | 1 + src/test_typing_extensions.py | 31 +++++++++++++++++++++++++- src/typing_extensions.py | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 55314d46..15200c35 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ This module currently contains the following: - `override` (see [PEP 698](https://peps.python.org/pep-0698/)) - The `default=` argument to `TypeVar`, `ParamSpec`, and `TypeVarTuple` (see [PEP 696](https://peps.python.org/pep-0696/)) - The `infer_variance=` argument to `TypeVar` (see [PEP 695](https://peps.python.org/pep-0695/)) + - The `@deprecated` decorator (see [PEP 702](https://peps.python.org/pep-0698/)) - In `typing` since Python 3.11 diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index d5982b9b..9013aeb9 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -29,7 +29,7 @@ from typing_extensions import assert_type, get_type_hints, get_origin, get_args from typing_extensions import clear_overloads, get_overloads, overload from typing_extensions import NamedTuple -from typing_extensions import override +from typing_extensions import override, deprecated from _typed_dict_test_helper import Foo, FooGeneric # Flags used to mark tests that only apply after a specific @@ -202,6 +202,35 @@ def static_method_bad_order(): self.assertIs(False, hasattr(Derived.static_method_bad_order, "__override__")) +class DeprecatedTests(BaseTestCase): + def test_deprecated(self): + @deprecated("A will go away soon") + class A: + pass + + self.assertEqual(A.__deprecated__, "A will go away soon") + self.assertIsInstance(A, type) + + @deprecated("b will go away soon") + def b(): + pass + + self.assertEqual(b.__deprecated__, "b will go away soon") + self.assertIsInstance(b, types.FunctionType) + + @overload + @deprecated("no more ints") + def h(x: int) -> int: ... + @overload + def h(x: str) -> str: ... + def h(x): + return x + + overloads = get_overloads(h) + self.assertEqual(len(overloads), 2) + self.assertEqual(overloads[0].__deprecated__, "no more ints") + + class AnyTests(BaseTestCase): def test_can_subclass(self): class Mock(Any): pass diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 03fe268e..479692f5 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -52,6 +52,7 @@ 'assert_type', 'clear_overloads', 'dataclass_transform', + 'deprecated', 'get_overloads', 'final', 'get_args', @@ -2128,6 +2129,47 @@ def method(self) -> None: return __arg +if hasattr(typing, "deprecated"): + override = typing.deprecated +else: + _T = typing.TypeVar("_T") + + def deprecated(__msg: str) -> typing.Callable[[_T], _T]: + """Indicate that a class, function or overload is deprecated. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @deprecated("int support is deprecated") + @overload + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + No runtime warning is issued. The decorator sets the ``__deprecated__`` + attribute on the decorated object to the deprecation message + passed to the decorator. + + See PEP 702 for details. + + """ + def decorator(__arg: _T) -> _T: + __arg.__deprecated__ = __msg + return __arg + + return decorator + + # We have to do some monkey patching to deal with the dual nature of # Unpack/TypeVarTuple: # - We want Unpack to be a kind of TypeVar so it gets accepted in From 0f08b6348fb739c8bcffff28e5d13544275e26da Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 31 Dec 2022 11:38:49 -0800 Subject: [PATCH 2/4] fix --- CHANGELOG.md | 4 +++- src/typing_extensions.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3cd4630..6fe6f920 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ -# Release 4.4.1 (?) +# Unreleased +- Runtime support for PEP 702, adding `typing_extensions.deprecated`. Patch + by Jelle Zijlstra. - Add better default value for TypeVar `default` parameter, PEP 696. Enables runtime check if `None` was passed as default. Patch by Marc Mueller (@cdce8p). diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 479692f5..ba8021b4 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2130,7 +2130,7 @@ def method(self) -> None: if hasattr(typing, "deprecated"): - override = typing.deprecated + deprecated = typing.deprecated else: _T = typing.TypeVar("_T") From 31be31684237c158f8217443453b555f8a45a183 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 1 Jan 2023 16:25:06 -0800 Subject: [PATCH 3/4] Update src/typing_extensions.py --- src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index cb460f5a..15a139d2 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2148,8 +2148,8 @@ class A: def f(): pass - @deprecated("int support is deprecated") @overload + @deprecated("int support is deprecated") def g(x: int) -> int: ... @overload def g(x: str) -> int: ... From f3bf9977d57f65453587119df04a8aa3c29849f5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 1 Jan 2023 19:38:58 -0800 Subject: [PATCH 4/4] discuss overloads --- src/typing_extensions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 15a139d2..c32c63d1 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2159,7 +2159,9 @@ def g(x: str) -> int: ... No runtime warning is issued. The decorator sets the ``__deprecated__`` attribute on the decorated object to the deprecation message - passed to the decorator. + passed to the decorator. If applied to an overload, the decorator + must be after the ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. See PEP 702 for details.