@@ -1647,6 +1647,95 @@ def test_final_forward_ref(self):
16471647 self .assertNotEqual (gth (Loop , globals ())['attr' ], Final [int ])
16481648 self .assertNotEqual (gth (Loop , globals ())['attr' ], Final )
16491649
1650+ def test_annotation_and_optional_default (self ):
1651+ annotation = Annotated [Union [int , None ], "data" ]
1652+ NoneAlias = None
1653+ StrAlias = str
1654+ T_default = TypeVar ("T_default" , default = None )
1655+ Ts = TypeVarTuple ("Ts" )
1656+
1657+ cases = {
1658+ # annotation: expected_type_hints
1659+ Annotated [None , "none" ] : Annotated [None , "none" ],
1660+ annotation : annotation ,
1661+ Optional [int ] : Optional [int ],
1662+ Optional [List [str ]] : Optional [List [str ]],
1663+ Optional [annotation ] : Optional [annotation ],
1664+ Union [str , None , str ] : Optional [str ],
1665+ Unpack [Tuple [int , None ]]: Unpack [Tuple [int , None ]],
1666+ # Note: A starred *Ts will use typing.Unpack in 3.11+ see Issue #485
1667+ Unpack [Ts ] : Unpack [Ts ],
1668+ }
1669+ # contains a ForwardRef, TypeVar(~prefix) or no expression
1670+ do_not_stringify_cases = {
1671+ () : {}, # Special-cased below to create an unannotated parameter
1672+ int : int ,
1673+ "int" : int ,
1674+ None : type (None ),
1675+ "NoneAlias" : type (None ),
1676+ List ["str" ] : List [str ],
1677+ Union [str , "str" ] : str ,
1678+ Union [str , None , "str" ] : Optional [str ],
1679+ Union [str , "NoneAlias" , "StrAlias" ]: Optional [str ],
1680+ Union [str , "Union[None, StrAlias]" ]: Optional [str ],
1681+ Union ["annotation" , T_default ] : Union [annotation , T_default ],
1682+ Annotated ["annotation" , "nested" ] : Annotated [Union [int , None ], "data" , "nested" ],
1683+ }
1684+ if TYPING_3_10_0 : # cannot construct UnionTypes before 3.10
1685+ do_not_stringify_cases ["str | NoneAlias | StrAlias" ] = str | None
1686+ cases [str | None ] = Optional [str ]
1687+ cases .update (do_not_stringify_cases )
1688+ for (annot , expected ), none_default , as_str , wrap_optional in itertools .product (
1689+ cases .items (), (False , True ), (False , True ), (False , True )
1690+ ):
1691+ # Special case:
1692+ skip_reason = None
1693+ annot_unchanged = annot
1694+ if sys .version_info [:2 ] == (3 , 10 ) and annot == "str | NoneAlias | StrAlias" and none_default :
1695+ # In 3.10 converts Optional[str | None] to Optional[str] which has a different repr
1696+ skip_reason = "UnionType not preserved in 3.10"
1697+ if wrap_optional :
1698+ if annot_unchanged == ():
1699+ continue
1700+ annot = Optional [annot ]
1701+ expected = {"x" : Optional [expected ]}
1702+ else :
1703+ expected = {"x" : expected } if annot_unchanged != () else {}
1704+ if as_str :
1705+ if annot_unchanged in do_not_stringify_cases or annot_unchanged == ():
1706+ continue
1707+ annot = str (annot )
1708+ with self .subTest (
1709+ annotation = annot ,
1710+ as_str = as_str ,
1711+ wrap_optional = wrap_optional ,
1712+ none_default = none_default ,
1713+ expected_type_hints = expected ,
1714+ ):
1715+ # Create function to check
1716+ if annot_unchanged == ():
1717+ if none_default :
1718+ def func (x = None ): pass
1719+ else :
1720+ def func (x ): pass
1721+ elif none_default :
1722+ def func (x : annot = None ): pass
1723+ else :
1724+ def func (x : annot ): pass
1725+ type_hints = get_type_hints (func , globals (), locals (), include_extras = True )
1726+ # Equality
1727+ self .assertEqual (type_hints , expected )
1728+ # Hash
1729+ for k in type_hints .keys ():
1730+ self .assertEqual (hash (type_hints [k ]), hash (expected [k ]))
1731+ # Test if UnionTypes are preserved
1732+ self .assertIs (type (type_hints [k ]), type (expected [k ]))
1733+ # Repr
1734+ with self .subTest ("Check str and repr" ):
1735+ if skip_reason == "UnionType not preserved in 3.10" :
1736+ self .skipTest (skip_reason )
1737+ self .assertEqual (repr (type_hints ), repr (expected ))
1738+
16501739
16511740class GetUtilitiesTestCase (TestCase ):
16521741 def test_get_origin (self ):
0 commit comments