10
10
import threading
11
11
import warnings
12
12
from pathlib import Path
13
- from typing import Any , Awaitable , Callable , Optional , TypeVar , Union
13
+ from types import FrameType
14
+ from typing import Awaitable , Callable , List , Optional , TypeVar , Union , cast
14
15
15
16
16
17
def ensure_dir_exists (path , mode = 0o777 ):
@@ -29,7 +30,7 @@ def ensure_dir_exists(path, mode=0o777):
29
30
raise OSError ("%r exists but is not a directory" % path )
30
31
31
32
32
- def _get_frame (level ) :
33
+ def _get_frame (level : int ) -> Optional [ FrameType ] :
33
34
"""Get the frame at the given stack level."""
34
35
# sys._getframe is much faster than inspect.stack, but isn't guaranteed to
35
36
# exist in all python implementations, so we fall back to inspect.stack()
@@ -49,7 +50,7 @@ def _get_frame(level):
49
50
# added in the process. For example, with the deprecation warning in the
50
51
# __init__ below, the appropriate stacklevel will change depending on how deep
51
52
# the inheritance hierarchy is.
52
- def _external_stacklevel (internal ) :
53
+ def _external_stacklevel (internal : List [ str ]) -> int :
53
54
"""Find the stacklevel of the first frame that doesn't contain any of the given internal strings
54
55
55
56
The depth will be 1 at minimum in order to start checking at the caller of
@@ -71,18 +72,21 @@ def _external_stacklevel(internal):
71
72
return level - 1
72
73
73
74
74
- def deprecation (message , internal = "jupyter_core/" ):
75
+ def deprecation (message : str , internal : Union [ str , List [ str ]] = "jupyter_core/" ) -> None :
75
76
"""Generate a deprecation warning targeting the first frame that is not 'internal'
76
77
77
78
internal is a string or list of strings, which if they appear in filenames in the
78
79
frames, the frames will be considered internal. Changing this can be useful if, for examnple,
79
80
we know that our internal code is calling out to another library.
80
81
"""
82
+ _internal : List [str ]
81
83
if isinstance (internal , str ):
82
- internal = [internal ]
84
+ _internal = [internal ]
85
+ else :
86
+ _internal = internal
83
87
84
88
# stack level of the first external frame from here
85
- stacklevel = _external_stacklevel (internal )
89
+ stacklevel = _external_stacklevel (_internal )
86
90
87
91
# The call to .warn adds one frame, so bump the stacklevel up by one
88
92
warnings .warn (message , DeprecationWarning , stacklevel = stacklevel + 1 )
@@ -129,7 +133,7 @@ def run(self, coro):
129
133
130
134
131
135
def run_sync (coro : Callable [..., Awaitable [T ]]) -> Callable [..., T ]:
132
- """Runs a coroutine and blocks until it has executed.
136
+ """Wraps coroutine in a function that blocks until it has executed.
133
137
134
138
Parameters
135
139
----------
@@ -165,18 +169,22 @@ def wrapped(*args, **kwargs):
165
169
return wrapped
166
170
167
171
168
- async def ensure_async (obj : Union [Awaitable [Any ], Any ]) -> Any :
172
+ async def ensure_async (obj : Union [Awaitable [T ], T ]) -> T :
169
173
"""Convert a non-awaitable object to a coroutine if needed,
170
174
and await it if it was not already awaited.
175
+
176
+ This function is meant to be called on the result of calling a function,
177
+ when that function could either be asynchronous or not.
171
178
"""
172
179
if inspect .isawaitable (obj ):
180
+ obj = cast (Awaitable [T ], obj )
173
181
try :
174
182
result = await obj
175
183
except RuntimeError as e :
176
184
if str (e ) == "cannot reuse already awaited coroutine" :
177
185
# obj is already the coroutine's result
178
- return obj
186
+ return cast ( T , obj )
179
187
raise
180
188
return result
181
189
# obj doesn't need to be awaited
182
- return obj
190
+ return cast ( T , obj )
0 commit comments