summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-03-01 18:01:27 (GMT)
committerGitHub <noreply@github.com>2024-03-01 18:01:27 (GMT)
commit90f75e1069f2d692480bcd305fc35b4fe7847e18 (patch)
tree7699f462cf551869091f80c8a2e5a7c26516607b /Lib/typing.py
parent16be4a3b93ca315d6d95a5e5dd17c81d03ed578a (diff)
downloadcpython-90f75e1069f2d692480bcd305fc35b4fe7847e18.zip
cpython-90f75e1069f2d692480bcd305fc35b4fe7847e18.tar.gz
cpython-90f75e1069f2d692480bcd305fc35b4fe7847e18.tar.bz2
[3.12] gh-112281: Allow `Union` with unhashable `Annotated` metadata (GH-112283) (#116213)
Co-authored-by: Nikita Sobolev <mail@sobolevn.me> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py45
1 files changed, 31 insertions, 14 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 1e4c725..7581c16 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -314,19 +314,33 @@ def _unpack_args(args):
newargs.append(arg)
return newargs
-def _deduplicate(params):
+def _deduplicate(params, *, unhashable_fallback=False):
# Weed out strict duplicates, preserving the first of each occurrence.
- all_params = set(params)
- if len(all_params) < len(params):
- new_params = []
- for t in params:
- if t in all_params:
- new_params.append(t)
- all_params.remove(t)
- params = new_params
- assert not all_params, all_params
- return params
-
+ try:
+ return dict.fromkeys(params)
+ except TypeError:
+ if not unhashable_fallback:
+ raise
+ # Happens for cases like `Annotated[dict, {'x': IntValidator()}]`
+ return _deduplicate_unhashable(params)
+
+def _deduplicate_unhashable(unhashable_params):
+ new_unhashable = []
+ for t in unhashable_params:
+ if t not in new_unhashable:
+ new_unhashable.append(t)
+ return new_unhashable
+
+def _compare_args_orderless(first_args, second_args):
+ first_unhashable = _deduplicate_unhashable(first_args)
+ second_unhashable = _deduplicate_unhashable(second_args)
+ t = list(second_unhashable)
+ try:
+ for elem in first_unhashable:
+ t.remove(elem)
+ except ValueError:
+ return False
+ return not t
def _remove_dups_flatten(parameters):
"""Internal helper for Union creation and substitution.
@@ -341,7 +355,7 @@ def _remove_dups_flatten(parameters):
else:
params.append(p)
- return tuple(_deduplicate(params))
+ return tuple(_deduplicate(params, unhashable_fallback=True))
def _flatten_literal_params(parameters):
@@ -1548,7 +1562,10 @@ class _UnionGenericAlias(_NotIterable, _GenericAlias, _root=True):
def __eq__(self, other):
if not isinstance(other, (_UnionGenericAlias, types.UnionType)):
return NotImplemented
- return set(self.__args__) == set(other.__args__)
+ try: # fast path
+ return set(self.__args__) == set(other.__args__)
+ except TypeError: # not hashable, slow path
+ return _compare_args_orderless(self.__args__, other.__args__)
def __hash__(self):
return hash(frozenset(self.__args__))