1
1
"""Tests for the notebook kernel and session manager."""
2
2
import asyncio
3
3
import concurrent .futures
4
+ import os
4
5
import sys
5
6
import uuid
6
7
from subprocess import PIPE
7
8
from unittest import TestCase
8
9
9
10
import pytest
11
+ from jupyter_core import paths
10
12
from tornado .testing import AsyncTestCase
11
13
from tornado .testing import gen_test
12
14
from traitlets .config .loader import Config
13
15
14
16
from ..localinterfaces import localhost
15
17
from .utils import AsyncKMSubclass
16
18
from .utils import AsyncMKMSubclass
19
+ from .utils import install_kernel
17
20
from .utils import skip_win32
18
21
from .utils import SyncKMSubclass
19
22
from .utils import SyncMKMSubclass
23
+ from .utils import test_env
20
24
from jupyter_client import AsyncKernelManager
21
25
from jupyter_client import KernelManager
22
26
from jupyter_client .multikernelmanager import AsyncMultiKernelManager
26
30
27
31
28
32
class TestKernelManager (TestCase ):
33
+ def setUp (self ):
34
+ self .env_patch = test_env ()
35
+ self .env_patch .start ()
36
+ super ().setUp ()
29
37
30
38
# static so picklable for multiprocessing on Windows
31
39
@staticmethod
@@ -58,6 +66,7 @@ def _run_lifecycle(km, test_kid=None):
58
66
else :
59
67
kid = km .start_kernel (stdout = PIPE , stderr = PIPE )
60
68
assert km .is_alive (kid )
69
+ assert km .get_kernel (kid ).ready .done ()
61
70
assert kid in km
62
71
assert kid in km .list_kernel_ids ()
63
72
assert len (km ) == 1 , f"{ len (km )} != { 1 } "
@@ -220,6 +229,10 @@ def test_subclass_callables(self):
220
229
221
230
222
231
class TestAsyncKernelManager (AsyncTestCase ):
232
+ def setUp (self ):
233
+ self .env_patch = test_env ()
234
+ self .env_patch .start ()
235
+ super ().setUp ()
223
236
224
237
# static so picklable for multiprocessing on Windows
225
238
@staticmethod
@@ -243,6 +256,13 @@ def _get_ipc_km():
243
256
km = AsyncMultiKernelManager (config = c )
244
257
return km
245
258
259
+ @staticmethod
260
+ def _get_pending_kernels_km ():
261
+ c = Config ()
262
+ c .AsyncMultiKernelManager .use_pending_kernels = True
263
+ km = AsyncMultiKernelManager (config = c )
264
+ return km
265
+
246
266
# static so picklable for multiprocessing on Windows
247
267
@staticmethod
248
268
async def _run_lifecycle (km , test_kid = None ):
@@ -334,6 +354,57 @@ async def test_shutdown_all_while_starting(self):
334
354
# shutdown again is okay, because we have no kernels
335
355
await km .shutdown_all ()
336
356
357
+ @gen_test
358
+ async def test_use_pending_kernels (self ):
359
+ km = self ._get_pending_kernels_km ()
360
+ kid = await km .start_kernel (stdout = PIPE , stderr = PIPE )
361
+ kernel = km .get_kernel (kid )
362
+ assert not kernel .ready .done ()
363
+ assert kid in km
364
+ assert kid in km .list_kernel_ids ()
365
+ assert len (km ) == 1 , f"{ len (km )} != { 1 } "
366
+ await kernel .ready
367
+ await km .restart_kernel (kid , now = True )
368
+ assert await km .is_alive (kid )
369
+ assert kid in km .list_kernel_ids ()
370
+ await km .interrupt_kernel (kid )
371
+ k = km .get_kernel (kid )
372
+ assert isinstance (k , AsyncKernelManager )
373
+ await km .shutdown_kernel (kid , now = True )
374
+ assert kid not in km , f"{ kid } not in { km } "
375
+
376
+ @gen_test
377
+ async def test_use_pending_kernels_early_restart (self ):
378
+ km = self ._get_pending_kernels_km ()
379
+ kid = await km .start_kernel (stdout = PIPE , stderr = PIPE )
380
+ kernel = km .get_kernel (kid )
381
+ assert not kernel .ready .done ()
382
+ with pytest .raises (RuntimeError ):
383
+ await km .restart_kernel (kid , now = True )
384
+ await kernel .ready
385
+ await km .shutdown_kernel (kid , now = True )
386
+ assert kid not in km , f"{ kid } not in { km } "
387
+
388
+ @gen_test
389
+ async def test_use_pending_kernels_early_shutdown (self ):
390
+ km = self ._get_pending_kernels_km ()
391
+ kid = await km .start_kernel (stdout = PIPE , stderr = PIPE )
392
+ kernel = km .get_kernel (kid )
393
+ assert not kernel .ready .done ()
394
+ await km .shutdown_kernel (kid , now = True )
395
+ assert kid not in km , f"{ kid } not in { km } "
396
+
397
+ @gen_test
398
+ async def test_use_pending_kernels_early_interrupt (self ):
399
+ km = self ._get_pending_kernels_km ()
400
+ kid = await km .start_kernel (stdout = PIPE , stderr = PIPE )
401
+ kernel = km .get_kernel (kid )
402
+ assert not kernel .ready .done ()
403
+ with pytest .raises (RuntimeError ):
404
+ await km .interrupt_kernel (kid )
405
+ await km .shutdown_kernel (kid , now = True )
406
+ assert kid not in km , f"{ kid } not in { km } "
407
+
337
408
@gen_test
338
409
async def test_tcp_cinfo (self ):
339
410
km = self ._get_tcp_km ()
@@ -466,3 +537,30 @@ async def test_subclass_callables(self):
466
537
assert mkm .call_count ("cleanup_resources" ) == 0
467
538
468
539
assert kid not in mkm , f"{ kid } not in { mkm } "
540
+
541
+ @gen_test
542
+ async def test_bad_kernelspec (self ):
543
+ km = self ._get_tcp_km ()
544
+ install_kernel (
545
+ os .path .join (paths .jupyter_data_dir (), "kernels" ),
546
+ argv = ["non_existent_executable" ],
547
+ name = "bad" ,
548
+ )
549
+ with pytest .raises (FileNotFoundError ):
550
+ await km .start_kernel (kernel_name = "bad" , stdout = PIPE , stderr = PIPE )
551
+
552
+ @gen_test
553
+ async def test_bad_kernelspec_pending (self ):
554
+ km = self ._get_pending_kernels_km ()
555
+ install_kernel (
556
+ os .path .join (paths .jupyter_data_dir (), "kernels" ),
557
+ argv = ["non_existent_executable" ],
558
+ name = "bad" ,
559
+ )
560
+ kernel_id = await km .start_kernel (kernel_name = "bad" , stdout = PIPE , stderr = PIPE )
561
+ assert kernel_id in km ._starting_kernels
562
+ with pytest .raises (FileNotFoundError ):
563
+ await km .get_kernel (kernel_id ).ready
564
+ assert kernel_id in km .list_kernel_ids ()
565
+ await km .shutdown_kernel (kernel_id )
566
+ assert kernel_id not in km .list_kernel_ids ()
0 commit comments