22import collections
33import collections .abc
44from collections import defaultdict
5- from functools import lru_cache , wraps
5+ from functools import lru_cache , wraps , reduce
66import gc
77import inspect
88import itertools
9+ import operator
910import pickle
1011import re
1112import sys
@@ -1770,6 +1771,26 @@ def test_union_union(self):
17701771 v = Union [u , Employee ]
17711772 self .assertEqual (v , Union [int , float , Employee ])
17721773
1774+ def test_union_of_unhashable (self ):
1775+ class UnhashableMeta (type ):
1776+ __hash__ = None
1777+
1778+ class A (metaclass = UnhashableMeta ): ...
1779+ class B (metaclass = UnhashableMeta ): ...
1780+
1781+ self .assertEqual (Union [A , B ].__args__ , (A , B ))
1782+ union1 = Union [A , B ]
1783+ with self .assertRaises (TypeError ):
1784+ hash (union1 )
1785+
1786+ union2 = Union [int , B ]
1787+ with self .assertRaises (TypeError ):
1788+ hash (union2 )
1789+
1790+ union3 = Union [A , int ]
1791+ with self .assertRaises (TypeError ):
1792+ hash (union3 )
1793+
17731794 def test_repr (self ):
17741795 self .assertEqual (repr (Union ), 'typing.Union' )
17751796 u = Union [Employee , int ]
@@ -5295,10 +5316,8 @@ def some(self):
52955316 self .assertFalse (hasattr (WithOverride .some , "__override__" ))
52965317
52975318 def test_multiple_decorators (self ):
5298- import functools
5299-
53005319 def with_wraps (f ): # similar to `lru_cache` definition
5301- @functools . wraps (f )
5320+ @wraps (f )
53025321 def wrapper (* args , ** kwargs ):
53035322 return f (* args , ** kwargs )
53045323 return wrapper
@@ -8183,6 +8202,76 @@ def test_flatten(self):
81838202 self .assertEqual (A .__metadata__ , (4 , 5 ))
81848203 self .assertEqual (A .__origin__ , int )
81858204
8205+ def test_deduplicate_from_union (self ):
8206+ # Regular:
8207+ self .assertEqual (get_args (Annotated [int , 1 ] | int ),
8208+ (Annotated [int , 1 ], int ))
8209+ self .assertEqual (get_args (Union [Annotated [int , 1 ], int ]),
8210+ (Annotated [int , 1 ], int ))
8211+ self .assertEqual (get_args (Annotated [int , 1 ] | Annotated [int , 2 ] | int ),
8212+ (Annotated [int , 1 ], Annotated [int , 2 ], int ))
8213+ self .assertEqual (get_args (Union [Annotated [int , 1 ], Annotated [int , 2 ], int ]),
8214+ (Annotated [int , 1 ], Annotated [int , 2 ], int ))
8215+ self .assertEqual (get_args (Annotated [int , 1 ] | Annotated [str , 1 ] | int ),
8216+ (Annotated [int , 1 ], Annotated [str , 1 ], int ))
8217+ self .assertEqual (get_args (Union [Annotated [int , 1 ], Annotated [str , 1 ], int ]),
8218+ (Annotated [int , 1 ], Annotated [str , 1 ], int ))
8219+
8220+ # Duplicates:
8221+ self .assertEqual (Annotated [int , 1 ] | Annotated [int , 1 ] | int ,
8222+ Annotated [int , 1 ] | int )
8223+ self .assertEqual (Union [Annotated [int , 1 ], Annotated [int , 1 ], int ],
8224+ Union [Annotated [int , 1 ], int ])
8225+
8226+ # Unhashable metadata:
8227+ self .assertEqual (get_args (str | Annotated [int , {}] | Annotated [int , set ()] | int ),
8228+ (str , Annotated [int , {}], Annotated [int , set ()], int ))
8229+ self .assertEqual (get_args (Union [str , Annotated [int , {}], Annotated [int , set ()], int ]),
8230+ (str , Annotated [int , {}], Annotated [int , set ()], int ))
8231+ self .assertEqual (get_args (str | Annotated [int , {}] | Annotated [str , {}] | int ),
8232+ (str , Annotated [int , {}], Annotated [str , {}], int ))
8233+ self .assertEqual (get_args (Union [str , Annotated [int , {}], Annotated [str , {}], int ]),
8234+ (str , Annotated [int , {}], Annotated [str , {}], int ))
8235+
8236+ self .assertEqual (get_args (Annotated [int , 1 ] | str | Annotated [str , {}] | int ),
8237+ (Annotated [int , 1 ], str , Annotated [str , {}], int ))
8238+ self .assertEqual (get_args (Union [Annotated [int , 1 ], str , Annotated [str , {}], int ]),
8239+ (Annotated [int , 1 ], str , Annotated [str , {}], int ))
8240+
8241+ import dataclasses
8242+ @dataclasses .dataclass
8243+ class ValueRange :
8244+ lo : int
8245+ hi : int
8246+ v = ValueRange (1 , 2 )
8247+ self .assertEqual (get_args (Annotated [int , v ] | None ),
8248+ (Annotated [int , v ], types .NoneType ))
8249+ self .assertEqual (get_args (Union [Annotated [int , v ], None ]),
8250+ (Annotated [int , v ], types .NoneType ))
8251+ self .assertEqual (get_args (Optional [Annotated [int , v ]]),
8252+ (Annotated [int , v ], types .NoneType ))
8253+
8254+ # Unhashable metadata duplicated:
8255+ self .assertEqual (Annotated [int , {}] | Annotated [int , {}] | int ,
8256+ Annotated [int , {}] | int )
8257+ self .assertEqual (Annotated [int , {}] | Annotated [int , {}] | int ,
8258+ int | Annotated [int , {}])
8259+ self .assertEqual (Union [Annotated [int , {}], Annotated [int , {}], int ],
8260+ Union [Annotated [int , {}], int ])
8261+ self .assertEqual (Union [Annotated [int , {}], Annotated [int , {}], int ],
8262+ Union [int , Annotated [int , {}]])
8263+
8264+ def test_order_in_union (self ):
8265+ expr1 = Annotated [int , 1 ] | str | Annotated [str , {}] | int
8266+ for args in itertools .permutations (get_args (expr1 )):
8267+ with self .subTest (args = args ):
8268+ self .assertEqual (expr1 , reduce (operator .or_ , args ))
8269+
8270+ expr2 = Union [Annotated [int , 1 ], str , Annotated [str , {}], int ]
8271+ for args in itertools .permutations (get_args (expr2 )):
8272+ with self .subTest (args = args ):
8273+ self .assertEqual (expr2 , Union [args ])
8274+
81868275 def test_specialize (self ):
81878276 L = Annotated [List [T ], "my decoration" ]
81888277 LI = Annotated [List [int ], "my decoration" ]
@@ -8203,6 +8292,16 @@ def test_hash_eq(self):
82038292 {Annotated [int , 4 , 5 ], Annotated [int , 4 , 5 ], Annotated [T , 4 , 5 ]},
82048293 {Annotated [int , 4 , 5 ], Annotated [T , 4 , 5 ]}
82058294 )
8295+ # Unhashable `metadata` raises `TypeError`:
8296+ a1 = Annotated [int , []]
8297+ with self .assertRaises (TypeError ):
8298+ hash (a1 )
8299+
8300+ class A :
8301+ __hash__ = None
8302+ a2 = Annotated [int , A ()]
8303+ with self .assertRaises (TypeError ):
8304+ hash (a2 )
82068305
82078306 def test_instantiate (self ):
82088307 class C :
0 commit comments