Skip to content

Commit 5f97e4e

Browse files
committed
work updates to tests
1 parent 73ae431 commit 5f97e4e

File tree

3 files changed

+86
-30
lines changed

3 files changed

+86
-30
lines changed

jupyter_client/manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class KernelManager(ConnectionFileMixin):
8989
def __init__(self, *args, **kwargs):
9090
super().__init__(**kwargs)
9191
self._shutdown_status = _ShutdownStatus.Unset
92+
# Create a place holder future.
9293
try:
9394
self._ready = Future()
9495
except RuntimeError:

jupyter_client/multikernelmanager.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,12 @@ def create_kernel_manager(*args, **kwargs) -> KernelManager:
9797

9898
context = Instance("zmq.Context")
9999

100-
_starting_kernels = Dict()
100+
_pending_kernels = Dict()
101+
102+
@property
103+
def _starting_kernels(self):
104+
"""A shim for backwards compatibility."""
105+
return self._pending_kernels
101106

102107
@default("context")
103108
def _context_default(self) -> zmq.Context:
@@ -165,7 +170,22 @@ async def _add_kernel_when_ready(
165170
await kernel_awaitable
166171
self._kernels[kernel_id] = km
167172
finally:
168-
self._starting_kernels.pop(kernel_id, None)
173+
self._pending_kernels.pop(kernel_id, None)
174+
175+
async def _remove_kernel_when_ready(
176+
self, kernel_id: str, kernel_awaitable: t.Awaitable
177+
) -> None:
178+
try:
179+
await kernel_awaitable
180+
self.remove_kernel(kernel_id)
181+
finally:
182+
self._pending_kernels.pop(kernel_id, None)
183+
184+
def _using_pending_kernels(self):
185+
"""Returns a boolean; a clearer method for determining if
186+
this multikernelmanager is using pending kernels or not
187+
"""
188+
return getattr(self, 'use_pending_kernels', False)
169189

170190
async def _async_start_kernel(self, kernel_name: t.Optional[str] = None, **kwargs) -> str:
171191
"""Start a new kernel.
@@ -186,12 +206,18 @@ async def _async_start_kernel(self, kernel_name: t.Optional[str] = None, **kwarg
186206

187207
starter = ensure_async(km.start_kernel(**kwargs))
188208
fut = asyncio.ensure_future(self._add_kernel_when_ready(kernel_id, km, starter))
189-
self._starting_kernels[kernel_id] = fut
209+
self._pending_kernels[kernel_id] = fut
190210

191-
if getattr(self, 'use_pending_kernels', False):
211+
# Handling a Pending Kernel
212+
if self._using_pending_kernels():
213+
# If using pending kernels, do not block
214+
# on the kernel start.
192215
self._kernels[kernel_id] = km
193216
else:
194217
await fut
218+
# raise an exception if one occurred during kernel startup.
219+
if km.ready.exception():
220+
raise km.ready.exception()
195221

196222
return kernel_id
197223

@@ -215,23 +241,28 @@ async def _async_shutdown_kernel(
215241
Will the kernel be restarted?
216242
"""
217243
kernel = self.get_kernel(kernel_id)
218-
if getattr(self, 'use_pending_kernels', False):
244+
if self._using_pending_kernels():
219245
# Make sure the previous kernel started before trying to shutdown.
220246
if not kernel.ready.done():
221247
raise RuntimeError("Kernel is in a pending state. Cannot shutdown.")
222248
# If the kernel didn't start properly, no need to shutdown.
223249
elif kernel.ready.exception():
250+
self.remove_kernel(kernel_id)
224251
return
225252
self.log.info("Kernel shutdown: %s" % kernel_id)
226-
if kernel_id in self._starting_kernels:
253+
if kernel_id in self._pending_kernels:
227254
try:
228-
await self._starting_kernels[kernel_id]
255+
await self._pending_kernels[kernel_id]
229256
except Exception:
230257
self.remove_kernel(kernel_id)
231258
return
232259
km = self.get_kernel(kernel_id)
233-
await ensure_async(km.shutdown_kernel(now, restart))
234-
self.remove_kernel(kernel_id)
260+
stopper = ensure_async(km.shutdown_kernel(now, restart))
261+
fut = asyncio.ensure_future(self._remove_kernel_when_ready(kernel_id, stopper))
262+
self._pending_kernels[kernel_id] = fut
263+
# Await the kernel if not using pending kernels.
264+
if not self._using_pending_kernels():
265+
await fut
235266

236267
shutdown_kernel = run_sync(_async_shutdown_kernel)
237268

@@ -266,13 +297,13 @@ def remove_kernel(self, kernel_id: str) -> KernelManager:
266297
async def _async_shutdown_all(self, now: bool = False) -> None:
267298
"""Shutdown all kernels."""
268299
kids = self.list_kernel_ids()
269-
kids += list(self._starting_kernels)
300+
kids += list(self._pending_kernels)
270301
futs = [ensure_async(self.shutdown_kernel(kid, now=now)) for kid in set(kids)]
271302
await asyncio.gather(*futs)
272303

273304
shutdown_all = run_sync(_async_shutdown_all)
274305

275-
@kernel_method
306+
# @kernel_method
276307
def interrupt_kernel(self, kernel_id: str) -> None:
277308
"""Interrupt (SIGINT) the kernel by its uuid.
278309
@@ -281,7 +312,12 @@ def interrupt_kernel(self, kernel_id: str) -> None:
281312
kernel_id : uuid
282313
The id of the kernel to interrupt.
283314
"""
315+
kernel = self.get_kernel(kernel_id)
316+
if not kernel.ready.done():
317+
raise RuntimeError("Kernel is in a pending state. Cannot interrupt.")
318+
out = kernel.interrupt_kernel()
284319
self.log.info("Kernel interrupted: %s" % kernel_id)
320+
return out
285321

286322
@kernel_method
287323
def signal_kernel(self, kernel_id: str, signum: int) -> None:
@@ -315,7 +351,7 @@ async def _async_restart_kernel(self, kernel_id: str, now: bool = False) -> None
315351
it is given a chance to perform a clean shutdown or not.
316352
"""
317353
kernel = self.get_kernel(kernel_id)
318-
if getattr(self, 'use_pending_kernels', False):
354+
if self._using_pending_kernels():
319355
if not kernel.ready.done():
320356
raise RuntimeError("Kernel is in a pending state. Cannot restart.")
321357
out = await ensure_async(kernel.restart_kernel(now=now))

jupyter_client/tests/test_multikernelmanager.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import sys
66
import uuid
7+
from asyncio import ensure_future
78
from subprocess import PIPE
89
from unittest import TestCase
910

@@ -354,7 +355,7 @@ async def test_shutdown_all_while_starting(self):
354355
self.assertNotIn(kid, km)
355356

356357
# Start another kernel
357-
kid = await km.start_kernel(stdout=PIPE, stderr=PIPE)
358+
kid = await ensure_future(km.start_kernel(stdout=PIPE, stderr=PIPE))
358359
self.assertIn(kid, km)
359360
self.assertEqual(len(km), 1)
360361
await km.shutdown_all()
@@ -365,52 +366,69 @@ async def test_shutdown_all_while_starting(self):
365366
@gen_test
366367
async def test_use_pending_kernels(self):
367368
km = self._get_pending_kernels_km()
368-
kid = await now(km.start_kernel(stdout=PIPE, stderr=PIPE))
369+
kid = await ensure_future(km.start_kernel(stdout=PIPE, stderr=PIPE))
369370
kernel = km.get_kernel(kid)
370371
assert not kernel.ready.done()
371372
assert kid in km
372373
assert kid in km.list_kernel_ids()
373374
assert len(km) == 1, f"{len(km)} != {1}"
374-
await now(kernel.ready)
375-
await now(km.restart_kernel(kid, now=True))
376-
assert await km.is_alive(kid)
375+
# Wait for the kernel to start.
376+
await kernel.ready
377+
await km.restart_kernel(kid, now=True)
378+
out = await km.is_alive(kid)
379+
assert out
377380
assert kid in km.list_kernel_ids()
378-
await now(km.interrupt_kernel(kid))
381+
await km.interrupt_kernel(kid)
379382
k = km.get_kernel(kid)
380383
assert isinstance(k, AsyncKernelManager)
381-
await now(km.shutdown_kernel(kid, now=True))
384+
await ensure_future(km.shutdown_kernel(kid, now=True))
385+
# Wait for the kernel to shutdown
386+
await kernel.ready
382387
assert kid not in km, f"{kid} not in {km}"
383388

384389
@gen_test
385390
async def test_use_pending_kernels_early_restart(self):
386391
km = self._get_pending_kernels_km()
387-
kid = await now(km.start_kernel(stdout=PIPE, stderr=PIPE))
392+
kid = await ensure_future(km.start_kernel(stdout=PIPE, stderr=PIPE))
388393
kernel = km.get_kernel(kid)
389394
assert not kernel.ready.done()
390395
with pytest.raises(RuntimeError):
391396
await km.restart_kernel(kid, now=True)
392-
await now(kernel.ready)
393-
await now(km.shutdown_kernel(kid, now=True))
397+
await kernel.ready
398+
await ensure_future(km.shutdown_kernel(kid, now=True))
399+
# Wait for the kernel to shutdown
400+
await kernel.ready
394401
assert kid not in km, f"{kid} not in {km}"
395402

396403
@gen_test
397404
async def test_use_pending_kernels_early_shutdown(self):
398405
km = self._get_pending_kernels_km()
399-
kid = await km.start_kernel(stdout=PIPE, stderr=PIPE)
406+
kid = await ensure_future(km.start_kernel(stdout=PIPE, stderr=PIPE))
400407
kernel = km.get_kernel(kid)
401408
assert not kernel.ready.done()
402-
await km.shutdown_kernel(kid, now=True)
409+
# Try shutting down while the kernel is pending
410+
with pytest.raises(RuntimeError):
411+
await ensure_future(km.shutdown_kernel(kid, now=True))
412+
await kernel.ready
413+
# Shutdown once the kernel is ready
414+
await ensure_future(km.shutdown_kernel(kid, now=True))
415+
# Wait for the kernel to shutdown
416+
await kernel.ready
403417
assert kid not in km, f"{kid} not in {km}"
404418

405419
@gen_test
406420
async def test_use_pending_kernels_early_interrupt(self):
407421
km = self._get_pending_kernels_km()
408-
kid = await km.start_kernel(stdout=PIPE, stderr=PIPE)
422+
kid = await ensure_future(km.start_kernel(stdout=PIPE, stderr=PIPE))
409423
kernel = km.get_kernel(kid)
410424
assert not kernel.ready.done()
411425
with pytest.raises(RuntimeError):
412426
await km.interrupt_kernel(kid)
413-
await km.shutdown_kernel(kid, now=True)
427+
# Now wait for the kernel to be ready.
428+
await kernel.ready
429+
await ensure_future(km.shutdown_kernel(kid, now=True))
430+
# Wait for the kernel to shutdown
431+
await kernel.ready
414432
assert kid not in km, f"{kid} not in {km}"
415433

416434
@gen_test
@@ -555,7 +573,7 @@ async def test_bad_kernelspec(self):
555573
name="bad",
556574
)
557575
with pytest.raises(FileNotFoundError):
558-
await km.start_kernel(kernel_name="bad", stdout=PIPE, stderr=PIPE)
576+
await ensure_future(km.start_kernel(kernel_name="bad", stdout=PIPE, stderr=PIPE))
559577

560578
@gen_test
561579
async def test_bad_kernelspec_pending(self):
@@ -565,10 +583,11 @@ async def test_bad_kernelspec_pending(self):
565583
argv=["non_existent_executable"],
566584
name="bad",
567585
)
568-
kernel_id = await km.start_kernel(kernel_name="bad", stdout=PIPE, stderr=PIPE)
569-
assert kernel_id in km._starting_kernels
586+
kernel_id = await ensure_future(
587+
km.start_kernel(kernel_name="bad", stdout=PIPE, stderr=PIPE)
588+
)
570589
with pytest.raises(FileNotFoundError):
571590
await km.get_kernel(kernel_id).ready
572591
assert kernel_id in km.list_kernel_ids()
573-
await km.shutdown_kernel(kernel_id)
592+
await ensure_future(km.shutdown_kernel(kernel_id))
574593
assert kernel_id not in km.list_kernel_ids()

0 commit comments

Comments
 (0)