@@ -57,37 +57,34 @@ class _ShutdownStatus(Enum):
57
57
F = t .TypeVar ('F' , bound = t .Callable [..., t .Any ])
58
58
59
59
60
- def in_pending_state (prefix : str = '' ) -> t .Callable [[F ], F ]:
61
- def decorator (method : F ) -> F :
62
- """Sets the kernel to a pending state by
63
- creating a fresh Future for the KernelManager's `ready`
64
- attribute. Once the method is finished, set the Future's results.
65
- """
66
-
67
- @t .no_type_check
68
- @functools .wraps (method )
69
- async def wrapper (self , * args , ** kwargs ):
70
- # Create a future for the decorated method
71
- name = f"{ prefix } _ready"
72
- future = getattr (self , name )
73
- if not future or future .done ():
74
- future = self ._future_factory ()
75
- setattr (self , name , future )
76
- try :
77
- # call wrapped method, await, and set the result or exception.
78
- out = await method (self , * args , ** kwargs )
79
- # Add a small sleep to ensure tests can capture the state before done
80
- await asyncio .sleep (0.01 )
81
- future .set_result (None )
82
- return out
83
- except Exception as e :
84
- future .set_exception (e )
85
- self .log .exception (future .exception ())
86
- raise e
60
+ def in_pending_state (method : F ) -> F :
61
+ """Sets the kernel to a pending state by
62
+ creating a fresh Future for the KernelManager's `ready`
63
+ attribute. Once the method is finished, set the Future's results.
64
+ """
87
65
88
- return t .cast (F , wrapper )
66
+ @t .no_type_check
67
+ @functools .wraps (method )
68
+ async def wrapper (self , * args , ** kwargs ):
69
+ # Create a future for the decorated method
70
+ try :
71
+ self ._ready = Future ()
72
+ except RuntimeError :
73
+ # No event loop running, use concurrent future
74
+ self ._ready = CFuture ()
75
+ try :
76
+ # call wrapped method, await, and set the result or exception.
77
+ out = await method (self , * args , ** kwargs )
78
+ # Add a small sleep to ensure tests can capture the state before done
79
+ await asyncio .sleep (0.01 )
80
+ self ._ready .set_result (None )
81
+ return out
82
+ except Exception as e :
83
+ self ._ready .set_exception (e )
84
+ self .log .exception (self ._ready .exception ())
85
+ raise e
89
86
90
- return decorator
87
+ return t . cast ( F , wrapper )
91
88
92
89
93
90
class KernelManager (ConnectionFileMixin ):
@@ -117,14 +114,18 @@ def _emit(self, *, action: str) -> None:
117
114
data = {"action" : action , "kernel_id" : self .kernel_id , "caller" : "kernel_manager" },
118
115
)
119
116
120
- _ready : t .Optional [CFuture ]
121
- _shutdown_ready : t .Optional [CFuture ]
117
+ _ready : t .Union [Future , CFuture ]
122
118
123
119
def __init__ (self , * args , ** kwargs ):
124
120
super ().__init__ (** kwargs )
125
121
self ._shutdown_status = _ShutdownStatus .Unset
126
- self ._ready = None
127
- self ._shutdown_ready = None
122
+ # Create a place holder future.
123
+ try :
124
+ asyncio .get_running_loop ()
125
+ self ._ready = Future ()
126
+ except RuntimeError :
127
+ # No event loop running, use concurrent future
128
+ self ._ready = CFuture ()
128
129
129
130
_created_context : Bool = Bool (False )
130
131
@@ -142,8 +143,6 @@ def _context_default(self) -> zmq.Context:
142
143
)
143
144
client_factory : Type = Type (klass = "jupyter_client.KernelClient" )
144
145
145
- _future_factory : t .Type [CFuture ] = CFuture
146
-
147
146
@default ("client_factory" ) # type:ignore[misc]
148
147
def _client_factory_default (self ) -> Type :
149
148
return import_item (self .client_class )
@@ -209,21 +208,10 @@ def _default_cache_ports(self) -> bool:
209
208
return self .transport == "tcp"
210
209
211
210
@property
212
- def ready (self ) -> CFuture :
213
- """A future that resolves when the kernel process has started."""
214
- if not self ._ready :
215
- self ._ready = self ._future_factory ()
216
- assert self ._ready is not None
211
+ def ready (self ) -> t .Union [CFuture , Future ]:
212
+ """A future that resolves when the kernel process has started for the first time"""
217
213
return self ._ready
218
214
219
- @property
220
- def shutdown_ready (self ) -> CFuture :
221
- """A future that resolves when the kernel process has shut down."""
222
- if not self ._shutdown_ready :
223
- self ._shutdown_ready = self ._future_factory ()
224
- assert self ._shutdown_ready is not None
225
- return self ._shutdown_ready
226
-
227
215
@property
228
216
def ipykernel (self ) -> bool :
229
217
return self .kernel_name in {"python" , "python2" , "python3" }
@@ -407,7 +395,7 @@ async def _async_post_start_kernel(self, **kw: t.Any) -> None:
407
395
408
396
post_start_kernel = run_sync (_async_post_start_kernel )
409
397
410
- @in_pending_state ()
398
+ @in_pending_state
411
399
async def _async_start_kernel (self , ** kw : t .Any ) -> None :
412
400
"""Starts a kernel on this host in a separate process.
413
401
@@ -503,7 +491,7 @@ async def _async_cleanup_resources(self, restart: bool = False) -> None:
503
491
504
492
cleanup_resources = run_sync (_async_cleanup_resources )
505
493
506
- @in_pending_state ( '_shutdown' )
494
+ @in_pending_state
507
495
async def _async_shutdown_kernel (self , now : bool = False , restart : bool = False ) -> None :
508
496
"""Attempts to stop the kernel process cleanly.
509
497
@@ -522,8 +510,6 @@ async def _async_shutdown_kernel(self, now: bool = False, restart: bool = False)
522
510
Will this kernel be restarted after it is shutdown. When this
523
511
is True, connection files will not be cleaned up.
524
512
"""
525
- # Reset the start ready future.
526
- self ._ready = self ._future_factory ()
527
513
self ._emit (action = "shutdown_started" )
528
514
self .shutting_down = True # Used by restarter to prevent race condition
529
515
# Stop monitoring for restarting while we shutdown.
@@ -696,8 +682,6 @@ class AsyncKernelManager(KernelManager):
696
682
# The PyZMQ Context to use for communication with the kernel.
697
683
context : Instance = Instance (zmq .asyncio .Context )
698
684
699
- _future_factory : t .Type [Future ] = Future # type:ignore[assignment]
700
-
701
685
@default ("context" ) # type:ignore[misc]
702
686
def _context_default (self ) -> zmq .asyncio .Context :
703
687
self ._created_context = True
0 commit comments