Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid error if origin has a buggy __eq__ #422

Merged
merged 6 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Unreleased

- Fix regression in v4.12.0 where specialization of certain
generics with an overridden `__eq__` method would raise errors.
Patch by Jelle Zijlstra.

# Release 4.12.1 (June 1, 2024)

- Preliminary changes for compatibility with the draft implementation
Expand Down
16 changes: 16 additions & 0 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6617,6 +6617,22 @@ def test_allow_default_after_non_default_in_alias(self):
a4 = Callable[[Unpack[Ts]], T]
self.assertEqual(a4.__args__, (Unpack[Ts], T))

@skip_if_py313_beta_1
def test_generic_with_broken_eq(self):
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
# See https://github.com/python/typing_extensions/pull/422 for context
class BrokenEq(type):
def __eq__(self, other):
if other is typing_extensions.Protocol:
raise TypeError("I'm broken")
return False

class G(Generic[T], metaclass=BrokenEq):
pass

alias = G[int]
self.assertIs(get_origin(alias), G)
self.assertEqual(get_args(alias), (int,))

@skipIf(
sys.version_info < (3, 11, 1),
"Not yet backported for older versions of Python"
Expand Down
17 changes: 12 additions & 5 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2954,13 +2954,20 @@ def _check_generic(cls, parameters, elen):
def _has_generic_or_protocol_as_origin() -> bool:
try:
frame = sys._getframe(2)
# not all platforms have sys._getframe()
except AttributeError:
# - Catch AttributeError: not all Python implementations have sys._getframe()
# - Catch ValueError: maybe we're called from an unexpected module
# and the call stack isn't deep enough
except (AttributeError, ValueError):
return False # err on the side of leniency
else:
return frame.f_locals.get("origin") in (
typing.Generic, Protocol, typing.Protocol
)
# If we somehow get invoked from outside typing.py,
# also err on the side of leniency
if frame.f_globals.get("__name__") != "typing":
return False
origin = frame.f_locals.get("origin")
# Cannot use "in" because origin may be an object with a buggy __eq__ that
# throws an error.
return origin is typing.Generic or origin is Protocol or origin is typing.Protocol


_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)}
Expand Down
Loading