@@ -1650,6 +1650,95 @@ def test_final_forward_ref(self):
16501650 self .assertNotEqual (gth (Loop , globals ())['attr' ], Final [int ])
16511651 self .assertNotEqual (gth (Loop , globals ())['attr' ], Final )
16521652
1653+ def test_annotation_and_optional_default (self ):
1654+ annotation = Annotated [Union [int , None ], "data" ]
1655+ NoneAlias = None
1656+ StrAlias = str
1657+ T_default = TypeVar ("T_default" , default = None )
1658+ Ts = TypeVarTuple ("Ts" )
1659+
1660+ cases = {
1661+ # annotation: expected_type_hints
1662+ Annotated [None , "none" ] : Annotated [None , "none" ],
1663+ annotation : annotation ,
1664+ Optional [int ] : Optional [int ],
1665+ Optional [List [str ]] : Optional [List [str ]],
1666+ Optional [annotation ] : Optional [annotation ],
1667+ Union [str , None , str ] : Optional [str ],
1668+ Unpack [Tuple [int , None ]]: Unpack [Tuple [int , None ]],
1669+ # Note: A starred *Ts will use typing.Unpack in 3.11+ see Issue #485
1670+ Unpack [Ts ] : Unpack [Ts ],
1671+ }
1672+ # contains a ForwardRef, TypeVar(~prefix) or no expression
1673+ do_not_stringify_cases = {
1674+ () : {}, # Special-cased below to create an unannotated parameter
1675+ int : int ,
1676+ "int" : int ,
1677+ None : type (None ),
1678+ "NoneAlias" : type (None ),
1679+ List ["str" ] : List [str ],
1680+ Union [str , "str" ] : str ,
1681+ Union [str , None , "str" ] : Optional [str ],
1682+ Union [str , "NoneAlias" , "StrAlias" ]: Optional [str ],
1683+ Union [str , "Union[None, StrAlias]" ]: Optional [str ],
1684+ Union ["annotation" , T_default ] : Union [annotation , T_default ],
1685+ Annotated ["annotation" , "nested" ] : Annotated [Union [int , None ], "data" , "nested" ],
1686+ }
1687+ if TYPING_3_10_0 : # cannot construct UnionTypes before 3.10
1688+ do_not_stringify_cases ["str | NoneAlias | StrAlias" ] = str | None
1689+ cases [str | None ] = Optional [str ]
1690+ cases .update (do_not_stringify_cases )
1691+ for (annot , expected ), none_default , as_str , wrap_optional in itertools .product (
1692+ cases .items (), (False , True ), (False , True ), (False , True )
1693+ ):
1694+ # Special case:
1695+ skip_reason = None
1696+ annot_unchanged = annot
1697+ if sys .version_info [:2 ] == (3 , 10 ) and annot == "str | NoneAlias | StrAlias" and none_default :
1698+ # In 3.10 converts Optional[str | None] to Optional[str] which has a different repr
1699+ skip_reason = "UnionType not preserved in 3.10"
1700+ if wrap_optional :
1701+ if annot_unchanged == ():
1702+ continue
1703+ annot = Optional [annot ]
1704+ expected = {"x" : Optional [expected ]}
1705+ else :
1706+ expected = {"x" : expected } if annot_unchanged != () else {}
1707+ if as_str :
1708+ if annot_unchanged in do_not_stringify_cases or annot_unchanged == ():
1709+ continue
1710+ annot = str (annot )
1711+ with self .subTest (
1712+ annotation = annot ,
1713+ as_str = as_str ,
1714+ wrap_optional = wrap_optional ,
1715+ none_default = none_default ,
1716+ expected_type_hints = expected ,
1717+ ):
1718+ # Create function to check
1719+ if annot_unchanged == ():
1720+ if none_default :
1721+ def func (x = None ): pass
1722+ else :
1723+ def func (x ): pass
1724+ elif none_default :
1725+ def func (x : annot = None ): pass
1726+ else :
1727+ def func (x : annot ): pass
1728+ type_hints = get_type_hints (func , globals (), locals (), include_extras = True )
1729+ # Equality
1730+ self .assertEqual (type_hints , expected )
1731+ # Hash
1732+ for k in type_hints .keys ():
1733+ self .assertEqual (hash (type_hints [k ]), hash (expected [k ]))
1734+ # Test if UnionTypes are preserved
1735+ self .assertIs (type (type_hints [k ]), type (expected [k ]))
1736+ # Repr
1737+ with self .subTest ("Check str and repr" ):
1738+ if skip_reason == "UnionType not preserved in 3.10" :
1739+ self .skipTest (skip_reason )
1740+ self .assertEqual (repr (type_hints ), repr (expected ))
1741+
16531742
16541743class GetUtilitiesTestCase (TestCase ):
16551744 def test_get_origin (self ):
@@ -4158,6 +4247,37 @@ def test_total(self):
41584247 self .assertEqual (Options .__required_keys__ , frozenset ())
41594248 self .assertEqual (Options .__optional_keys__ , {'log_level' , 'log_path' })
41604249
4250+ def test_total_inherits_non_total (self ):
4251+ class TD1 (TypedDict , total = False ):
4252+ a : int
4253+
4254+ self .assertIs (TD1 .__total__ , False )
4255+
4256+ class TD2 (TD1 ):
4257+ b : str
4258+
4259+ self .assertIs (TD2 .__total__ , True )
4260+
4261+ def test_total_with_assigned_value (self ):
4262+ class TD (TypedDict ):
4263+ __total__ = "some_value"
4264+
4265+ self .assertIs (TD .__total__ , True )
4266+
4267+ class TD2 (TypedDict , total = True ):
4268+ __total__ = "some_value"
4269+
4270+ self .assertIs (TD2 .__total__ , True )
4271+
4272+ class TD3 (TypedDict , total = False ):
4273+ __total__ = "some value"
4274+
4275+ self .assertIs (TD3 .__total__ , False )
4276+
4277+ TD4 = TypedDict ('TD4' , {'__total__' : "some_value" }) # noqa: F821
4278+ self .assertIs (TD4 .__total__ , True )
4279+
4280+
41614281 def test_optional_keys (self ):
41624282 class Point2Dor3D (Point2D , total = False ):
41634283 z : int
@@ -6156,6 +6276,12 @@ def test_equivalent_nested_variadics(self):
61566276 self .assertEqual (nested_tuple_bare , TupleAliasTsT [Unpack [Tuple [str , int ]], object ])
61576277 self .assertEqual (nested_tuple_bare , TupleAliasTsT [Unpack [Tuple [str ]], Unpack [Tuple [int ]], object ])
61586278
6279+ @skipUnless (TYPING_3_11_0 , "Needed for backport" )
6280+ def test_type_var_inheritance (self ):
6281+ Ts = TypeVarTuple ("Ts" )
6282+ self .assertFalse (isinstance (Unpack [Ts ], TypeVar ))
6283+ self .assertFalse (isinstance (Unpack [Ts ], typing .TypeVar ))
6284+
61596285
61606286class TypeVarTupleTests (BaseTestCase ):
61616287
0 commit comments