1
1
from __future__ import annotations as _annotations
2
2
3
3
import asyncio
4
- import sys
5
4
import types
6
5
from datetime import datetime , timezone
7
- from typing import Annotated , Any , TypeVar , Union , get_args , get_origin
6
+ from typing import Any , TypeVar
8
7
9
- import typing_extensions
8
+ from typing_extensions import TypeIs , get_args , get_origin
9
+ from typing_inspection import typing_objects
10
+ from typing_inspection .introspection import is_union_origin
10
11
11
12
12
13
def get_event_loop ():
@@ -19,13 +20,13 @@ def get_event_loop():
19
20
20
21
21
22
def get_union_args (tp : Any ) -> tuple [Any , ...]:
22
- """Extract the arguments of a Union type if `response_type` is a union, otherwise return the original type ."""
23
+ """Extract the arguments of a Union type if `response_type` is a union, otherwise return an empty tuple ."""
23
24
# similar to `pydantic_ai_slim/pydantic_ai/_result.py:get_union_args`
24
- if isinstance (tp , typing_extensions . TypeAliasType ):
25
+ if typing_objects . is_typealiastype (tp ):
25
26
tp = tp .__value__
26
27
27
28
origin = get_origin (tp )
28
- if origin_is_union (origin ):
29
+ if is_union_origin (origin ):
29
30
return get_args (tp )
30
31
else :
31
32
return (tp ,)
@@ -38,35 +39,13 @@ def unpack_annotated(tp: Any) -> tuple[Any, list[Any]]:
38
39
`(tp argument, ())` if not annotated, otherwise `(stripped type, annotations)`.
39
40
"""
40
41
origin = get_origin (tp )
41
- if origin is Annotated or origin is typing_extensions . Annotated :
42
+ if typing_objects . is_annotated ( origin ) :
42
43
inner_tp , * args = get_args (tp )
43
44
return inner_tp , args
44
45
else :
45
46
return tp , []
46
47
47
48
48
- def is_never (tp : Any ) -> bool :
49
- """Check if a type is `Never`."""
50
- if tp is typing_extensions .Never :
51
- return True
52
- elif typing_never := getattr (typing_extensions , 'Never' , None ):
53
- return tp is typing_never
54
- else :
55
- return False
56
-
57
-
58
- # same as `pydantic_ai_slim/pydantic_ai/_result.py:origin_is_union`
59
- if sys .version_info < (3 , 10 ):
60
-
61
- def origin_is_union (tp : type [Any ] | None ) -> bool :
62
- return tp is Union
63
-
64
- else :
65
-
66
- def origin_is_union (tp : type [Any ] | None ) -> bool :
67
- return tp is Union or tp is types .UnionType
68
-
69
-
70
49
def comma_and (items : list [str ]) -> str :
71
50
"""Join with a comma and 'and' for the last item."""
72
51
if len (items ) == 1 :
@@ -84,7 +63,11 @@ def get_parent_namespace(frame: types.FrameType | None) -> dict[str, Any] | None
84
63
"""
85
64
if frame is not None :
86
65
if back := frame .f_back :
87
- if back .f_code .co_filename .endswith ('/typing.py' ):
66
+ if back .f_globals .get ('__name__' ) == 'typing' :
67
+ # If the class calling this function is generic, explicitly parameterizing the class
68
+ # results in a `typing._GenericAlias` instance, which proxies instantiation calls to the
69
+ # "real" class and thus adding an extra frame to the call. To avoid pulling anything
70
+ # from the `typing` module, use the correct frame (the one before):
88
71
return get_parent_namespace (back )
89
72
else :
90
73
return back .f_locals
@@ -107,5 +90,5 @@ class Unset:
107
90
T = TypeVar ('T' )
108
91
109
92
110
- def is_set (t_or_unset : T | Unset ) -> typing_extensions . TypeGuard [T ]:
93
+ def is_set (t_or_unset : T | Unset ) -> TypeIs [T ]:
111
94
return t_or_unset is not UNSET
0 commit comments