11"""Idea is taken from: https://stackoverflow.com/a/55504010/10975692"""
22import inspect
3+ import sys
34import types
45import typing
56from io import BytesIO , StringIO , BufferedWriter , TextIOWrapper
@@ -219,9 +220,7 @@ def _is_instance(obj: Any, type_: Any, type_vars: Dict[TypeVar_, Any], context:
219220 return isinstance (obj , type_ .__supertype__ )
220221
221222 if hasattr (obj , '_asdict' ):
222- if hasattr (type_ , '_field_types' ):
223- field_types = type_ ._field_types
224- elif hasattr (type_ , '__annotations__' ):
223+ if hasattr (type_ , '__annotations__' ):
225224 field_types = type_ .__annotations__
226225 else :
227226 return False
@@ -338,7 +337,8 @@ def _is_generic(cls: Any) -> bool:
338337 return True
339338 elif isinstance (cls , typing ._SpecialForm ):
340339 return cls not in {Any }
341-
340+ elif cls is typing .Union or type (cls ) is typing .Union : # for python >= 3.14 Union is no longer a typing._SpecialForm
341+ return True
342342 return False
343343
344344
@@ -404,8 +404,6 @@ def get_type_arguments(cls: Any) -> Tuple[Any, ...]:
404404 (typing.Tuple[float, str],)
405405 >>> get_type_arguments(List[Tuple[Any, ...]])
406406 (typing.Tuple[typing.Any, ...],)
407- >>> Union[bool, int, float]
408- typing.Union[bool, int, float]
409407 >>> get_type_arguments(Union[str, float, int])
410408 (<class 'str'>, <class 'float'>, <class 'int'>)
411409 >>> get_type_arguments(Union[str, float, List[int], int])
@@ -472,10 +470,10 @@ def get_base_generic(cls: Any) -> Any:
472470 typing.Dict
473471 >>> get_base_generic(Dict[str, str])
474472 typing.Dict
475- >>> get_base_generic(Union)
476- typing.Union
477- >>> get_base_generic(Union[float, int, str])
478- typing.Union
473+ >>> 'typing.Union' in str( get_base_generic(Union)) # 3.13: typing.Union 3.14: <class 'typing.Union'>
474+ True
475+ >>> 'typing.Union' in str( get_base_generic(Union[float, int, str])) # 3.13: typing.Union 3.14: <class 'typing.Union'>
476+ True
479477 >>> get_base_generic(Set)
480478 typing.Set
481479 >>> get_base_generic(Set[int])
@@ -491,7 +489,7 @@ def get_base_generic(cls: Any) -> Any:
491489
492490 if name is not None :
493491 return getattr (typing , name )
494- elif origin is not None :
492+ elif origin is not None and cls is not typing . Union :
495493 return origin
496494 return cls
497495
@@ -537,10 +535,8 @@ def _is_subtype(sub_type: Any, super_type: Any, context: Dict[str, Any] = None)
537535 False
538536 >>> _is_subtype(List[int], List[Union[int, float]])
539537 True
540- >>> _is_subtype(List[Union[int, float]], List[int])
541- Traceback (most recent call last):
542- ...
543- TypeError: issubclass() arg 1 must be a class
538+ >>> _is_subtype(List[Union[int, float]], List[int]) if sys.version_info >= (3, 14) else False
539+ False
544540 >>> class Parent: pass
545541 >>> class Child(Parent): pass
546542 >>> _is_subtype(List[Child], List[Parent])
@@ -740,9 +736,6 @@ def _instancecheck_tuple(tup: Tuple, type_args: Any, type_vars: Dict[TypeVar_, A
740736 if Ellipsis in type_args :
741737 return all (_is_instance (obj = val , type_ = type_args [0 ], type_vars = type_vars , context = context ) for val in tup )
742738
743- if tup == () and type_args == ((),):
744- return True
745-
746739 if len (tup ) != len (type_args ):
747740 return False
748741
@@ -1033,6 +1026,8 @@ def convert_to_typing_types(x: typing.Type) -> typing.Type:
10331026 typing.Tuple[int]
10341027 >>> convert_to_typing_types(type[int])
10351028 typing.Type[int]
1029+ >>> convert_to_typing_types(type[int | float])
1030+ typing.Type[int | float]
10361031 >>> convert_to_typing_types(tuple[int, float])
10371032 typing.Tuple[int, float]
10381033 >>> convert_to_typing_types(dict[int, float])
@@ -1064,6 +1059,8 @@ def convert_to_typing_types(x: typing.Type) -> typing.Type:
10641059 return typing .FrozenSet [tuple (args )]
10651060 elif origin is type :
10661061 return typing .Type [tuple (args )]
1062+ elif origin is typing .Union :
1063+ return x # new since Python 3.14
10671064
10681065 raise RuntimeError (x )
10691066
0 commit comments