Skip to content

Commit 53bcdde

Browse files
Avoid error if origin has a buggy __eq__ (#422)
Fixes #419 Co-authored-by: Alex Waygood <[email protected]>
1 parent 7269638 commit 53bcdde

File tree

3 files changed

+34
-5
lines changed

3 files changed

+34
-5
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Unreleased
2+
3+
- Fix regression in v4.12.0 where specialization of certain
4+
generics with an overridden `__eq__` method would raise errors.
5+
Patch by Jelle Zijlstra.
6+
17
# Release 4.12.1 (June 1, 2024)
28

39
- Preliminary changes for compatibility with the draft implementation

src/test_typing_extensions.py

+16
Original file line numberDiff line numberDiff line change
@@ -6617,6 +6617,22 @@ def test_allow_default_after_non_default_in_alias(self):
66176617
a4 = Callable[[Unpack[Ts]], T]
66186618
self.assertEqual(a4.__args__, (Unpack[Ts], T))
66196619

6620+
@skip_if_py313_beta_1
6621+
def test_generic_with_broken_eq(self):
6622+
# See https://github.com/python/typing_extensions/pull/422 for context
6623+
class BrokenEq(type):
6624+
def __eq__(self, other):
6625+
if other is typing_extensions.Protocol:
6626+
raise TypeError("I'm broken")
6627+
return False
6628+
6629+
class G(Generic[T], metaclass=BrokenEq):
6630+
pass
6631+
6632+
alias = G[int]
6633+
self.assertIs(get_origin(alias), G)
6634+
self.assertEqual(get_args(alias), (int,))
6635+
66206636
@skipIf(
66216637
sys.version_info < (3, 11, 1),
66226638
"Not yet backported for older versions of Python"

src/typing_extensions.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -2954,13 +2954,20 @@ def _check_generic(cls, parameters, elen):
29542954
def _has_generic_or_protocol_as_origin() -> bool:
29552955
try:
29562956
frame = sys._getframe(2)
2957-
# not all platforms have sys._getframe()
2958-
except AttributeError:
2957+
# - Catch AttributeError: not all Python implementations have sys._getframe()
2958+
# - Catch ValueError: maybe we're called from an unexpected module
2959+
# and the call stack isn't deep enough
2960+
except (AttributeError, ValueError):
29592961
return False # err on the side of leniency
29602962
else:
2961-
return frame.f_locals.get("origin") in (
2962-
typing.Generic, Protocol, typing.Protocol
2963-
)
2963+
# If we somehow get invoked from outside typing.py,
2964+
# also err on the side of leniency
2965+
if frame.f_globals.get("__name__") != "typing":
2966+
return False
2967+
origin = frame.f_locals.get("origin")
2968+
# Cannot use "in" because origin may be an object with a buggy __eq__ that
2969+
# throws an error.
2970+
return origin is typing.Generic or origin is Protocol or origin is typing.Protocol
29642971

29652972

29662973
_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)}

0 commit comments

Comments
 (0)