From bae017a5376988682cbb0b6119a6e2d8bbecab22 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:12:31 +0200 Subject: [PATCH 1/3] Do not shadow user arguments in generated `__new__` by `@deprecated` Backport of: https://github.com/python/cpython/pull/132160 --- src/test_typing_extensions.py | 19 +++++++++++++++++++ src/typing_extensions.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index b8f5d4b7..584b0fa4 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -707,6 +707,25 @@ class Child(Base, Mixin): instance = Child(42) self.assertEqual(instance.a, 42) + def test_do_not_shadow_user_arguments(self): + new_called = False + new_called_cls = None + + @deprecated("MyMeta will go away soon") + class MyMeta(type): + def __new__(mcs, name, bases, attrs, cls=None): + nonlocal new_called, new_called_cls + new_called = True + new_called_cls = cls + return super().__new__(mcs, name, bases, attrs) + + with self.assertWarnsRegex(DeprecationWarning, "MyMeta will go away soon"): + class Foo(metaclass=MyMeta, cls='haha'): + pass + + self.assertTrue(new_called) + self.assertEqual(new_called_cls, 'haha') + def test_existing_init_subclass(self): @deprecated("C will go away soon") class C: diff --git a/src/typing_extensions.py b/src/typing_extensions.py index c6c3b88e..cc0ecd51 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3203,7 +3203,7 @@ def __call__(self, arg: _T, /) -> _T: original_new = arg.__new__ @functools.wraps(original_new) - def __new__(cls, *args, **kwargs): + def __new__(cls, /, *args, **kwargs): if cls is arg: warnings.warn(msg, category=category, stacklevel=stacklevel + 1) if original_new is not object.__new__: From 4e4b0cef536745c572c1254dcfb2184778f266c5 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:14:52 +0200 Subject: [PATCH 2/3] Fix conditional --- src/typing_extensions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index cc0ecd51..fa89c83e 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3123,7 +3123,8 @@ def method(self) -> None: return arg -if hasattr(warnings, "deprecated"): +# Python 3.13.3+ contains a fix for the wrapped __new__ +if sys.version_info >= (3, 13, 3): deprecated = warnings.deprecated else: _T = typing.TypeVar("_T") From 5ffdb2c5d66a78f03f192968b7b8f04bcf32c38f Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:06:03 +0200 Subject: [PATCH 3/3] Add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d7f109c..ab520c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ - Fix `TypeError` when taking the union of `typing_extensions.TypeAliasType` and a `typing.TypeAliasType` on Python 3.12 and 3.13. Patch by [Joren Hammudoglu](https://github.com/jorenham). +- Backport from CPython PR [#132160](https://github.com/python/cpython/pull/132160) + to avoid having user arguments shadowed in generated `__new__` by + `@typing_extensions.deprecated`. + Patch by [Victorien Plot](https://github.com/Viicos). # Release 4.13.1 (April 3, 2025)