From 04d81e8a75b8e7cf6a72338807f638c198c210a6 Mon Sep 17 00:00:00 2001 From: dave-shawley Date: Fri, 9 Feb 2024 17:11:37 -0500 Subject: [PATCH 1/2] gh-115165: Fix `typing.Annotated` for immutable types (GH-115213) The return value from an annotated callable can raise any exception from __setattr__ for the `__orig_class__` property. (cherry picked from commit 564385612cdf72c2fa8e629a68225fb2cd3b3d99) Co-authored-by: dave-shawley --- Lib/test/test_typing.py | 21 +++++++++++++++++++ Lib/typing.py | 4 +++- ...-02-09-07-20-16.gh-issue-115165.yfJLXA.rst | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-09-07-20-16.gh-issue-115165.yfJLXA.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b0e3ca96fd1c7f..20c1ea226137ad 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3665,6 +3665,16 @@ class C(B[int]): c.bar = 'abc' self.assertEqual(c.__dict__, {'bar': 'abc'}) + def test_setattr_exceptions(self): + class Immutable[T]: + def __setattr__(self, key, value): + raise RuntimeError("immutable") + + # gh-115165: This used to cause RuntimeError to be raised + # when we tried to set `__orig_class__` on the `Immutable` instance + # returned by the `Immutable[int]()` call + self.assertIsInstance(Immutable[int](), Immutable) + def test_subscripted_generics_as_proxies(self): T = TypeVar('T') class C(Generic[T]): @@ -7397,6 +7407,17 @@ def test_instantiate_generic(self): self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1}) self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1}) + def test_instantiate_immutable(self): + class C: + def __setattr__(self, key, value): + raise Exception("should be ignored") + + A = Annotated[C, "a decoration"] + # gh-115165: This used to cause RuntimeError to be raised + # when we tried to set `__orig_class__` on the `C` instance + # returned by the `A()` call + self.assertIsInstance(A(), C) + def test_cannot_instantiate_forward(self): A = Annotated["int", (5, 6)] with self.assertRaises(TypeError): diff --git a/Lib/typing.py b/Lib/typing.py index 52898189504c78..c62993dbd515f8 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1275,7 +1275,9 @@ def __call__(self, *args, **kwargs): result = self.__origin__(*args, **kwargs) try: result.__orig_class__ = self - except AttributeError: + # Some objects raise TypeError (or something even more exotic) + # if you try to set attributes on them; we guard against that here + except Exception: pass return result diff --git a/Misc/NEWS.d/next/Library/2024-02-09-07-20-16.gh-issue-115165.yfJLXA.rst b/Misc/NEWS.d/next/Library/2024-02-09-07-20-16.gh-issue-115165.yfJLXA.rst new file mode 100644 index 00000000000000..73d3d001f07f3f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-09-07-20-16.gh-issue-115165.yfJLXA.rst @@ -0,0 +1,4 @@ +Most exceptions are now ignored when attempting to set the ``__orig_class__`` +attribute on objects returned when calling :mod:`typing` generic aliases +(including generic aliases created using :data:`typing.Annotated`). +Previously only :exc:`AttributeError`` was ignored. Patch by Dave Shawley. From 2c18de124f914fa79e8f8d7564c90b71c49b4b5f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 9 Feb 2024 22:26:27 +0000 Subject: [PATCH 2/2] no pep-695 for you on Python 3.11 Co-authored-by: Kirill Podoprigora --- Lib/test/test_typing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 20c1ea226137ad..8d6d693ed08437 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3666,7 +3666,8 @@ class C(B[int]): self.assertEqual(c.__dict__, {'bar': 'abc'}) def test_setattr_exceptions(self): - class Immutable[T]: + T = TypeVar("T") + class Immutable(Generic[T]): def __setattr__(self, key, value): raise RuntimeError("immutable")