@@ -1236,10 +1236,75 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
12361236 )
12371237 else : # 3.8
12381238 hint = typing .get_type_hints (obj , globalns = globalns , localns = localns )
1239+ if sys .version_info < (3 , 11 ) and hint :
1240+ hint = _clean_optional (obj , hint , globalns , localns )
12391241 if include_extras :
12401242 return hint
12411243 return {k : _strip_extras (t ) for k , t in hint .items ()}
12421244
1245+ _NoneType = type (None )
1246+
1247+ def _could_be_inserted_optional (t ):
1248+ """detects Union[..., None] pattern"""
1249+ # 3.8+ compatible checking before _UnionGenericAlias
1250+ if not hasattr (t , "__origin__" ) or t .__origin__ is not Union :
1251+ return False
1252+ # Assume if last argument is not None they are user defined
1253+ if t .__args__ [- 1 ] is not _NoneType :
1254+ return False
1255+ return True
1256+
1257+ # < 3.11
1258+ def _clean_optional (obj , hints , globalns = None , localns = None ):
1259+ # reverts injected Union[..., None] cases from typing.get_type_hints
1260+ # when a None default value is used.
1261+ # see https://github.com/python/typing_extensions/issues/310
1262+ original_hints = getattr (obj , '__annotations__' , None )
1263+ defaults = typing ._get_defaults (obj )
1264+ for name , value in hints .items ():
1265+ # Not a Union[..., None] or replacement conditions not fullfilled
1266+ if (not _could_be_inserted_optional (value )
1267+ or name not in defaults
1268+ or defaults [name ] is not None
1269+ ):
1270+ continue
1271+ original_value = original_hints [name ]
1272+ if original_value is None :
1273+ original_value = _NoneType
1274+ # Forward reference
1275+ if isinstance (original_value , str ):
1276+ if globalns is None :
1277+ if isinstance (obj , _types .ModuleType ):
1278+ globalns = obj .__dict__
1279+ else :
1280+ nsobj = obj
1281+ # Find globalns for the unwrapped object.
1282+ while hasattr (nsobj , '__wrapped__' ):
1283+ nsobj = nsobj .__wrapped__
1284+ globalns = getattr (nsobj , '__globals__' , {})
1285+ if localns is None :
1286+ localns = globalns
1287+ elif localns is None :
1288+ localns = globalns
1289+ if sys .version_info < (3 , 9 ):
1290+ ref = ForwardRef (original_value )
1291+ else :
1292+ ref = ForwardRef (
1293+ original_value ,
1294+ is_argument = not isinstance (obj , _types .ModuleType )
1295+ )
1296+ original_value = typing ._eval_type (ref , globalns , localns )
1297+ # Values was not modified or original is already Optional
1298+ if original_value == value or _could_be_inserted_optional (original_value ):
1299+ continue
1300+ # NoneType was added to value
1301+ if len (value .__args__ ) == 2 :
1302+ hints [name ] = value .__args__ [0 ] # not a Union
1303+ else :
1304+ hints [name ] = Union [value .__args__ [:- 1 ]] # still a Union
1305+
1306+ return hints
1307+
12431308
12441309# Python 3.9+ has PEP 593 (Annotated)
12451310if hasattr (typing , 'Annotated' ):
0 commit comments