Skip to content

Commit c897015

Browse files
kevin-batestoonijn
authored andcommitted
Convert to async/await and apply touch ups
1 parent 1c0e0c8 commit c897015

File tree

4 files changed

+50
-18
lines changed

4 files changed

+50
-18
lines changed

notebook/notebookapp.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from __future__ import absolute_import, print_function
88

99
import notebook
10+
import asyncio
1011
import binascii
1112
import datetime
1213
import errno
@@ -583,7 +584,7 @@ class NotebookApp(JupyterApp):
583584
flags = flags
584585

585586
classes = [
586-
KernelManager, Session, MappingKernelManager, KernelSpecManager,
587+
KernelManager, Session, MappingKernelManager, AsyncMappingKernelManager, KernelSpecManager,
587588
ContentsManager, FileContentsManager, NotebookNotary,
588589
GatewayKernelManager, GatewayKernelSpecManager, GatewaySessionManager, GatewayClient,
589590
]
@@ -1392,10 +1393,10 @@ def init_configurables(self):
13921393
# Ensure the appropriate jupyter_client is in place.
13931394
if isinstance(self.kernel_manager, AsyncMappingKernelManager):
13941395
if not async_kernel_mgmt_available:
1395-
raise ValueError("You're using `AsyncMappingKernelManager` without an appropriate "
1396+
raise ValueError("You are using `AsyncMappingKernelManager` without an appropriate "
13961397
"jupyter_client installed! Upgrade jupyter_client or change kernel managers.")
13971398
else:
1398-
self.log.info("Asynchronous kernel management has been configured via '{}'.".
1399+
self.log.info("Asynchronous kernel management has been configured to use '{}'.".
13991400
format(self.kernel_manager.__class__.__name__))
14001401

14011402
self.contents_manager = self.contents_manager_class(
@@ -1800,7 +1801,11 @@ def cleanup_kernels(self):
18001801
n_kernels = len(self.kernel_manager.list_kernel_ids())
18011802
kernel_msg = trans.ngettext('Shutting down %d kernel', 'Shutting down %d kernels', n_kernels)
18021803
self.log.info(kernel_msg % n_kernels)
1803-
self.kernel_manager.shutdown_all()
1804+
# If we're using async kernel management, we need to invoke the async method via the event loop.
1805+
if isinstance(self.kernel_manager, AsyncMappingKernelManager):
1806+
asyncio.get_event_loop().run_until_complete(self.kernel_manager.shutdown_all())
1807+
else:
1808+
self.kernel_manager.shutdown_all()
18041809

18051810
def notebook_info(self, kernel_count=True):
18061811
"Return the current working directory and the server url information"

notebook/services/kernels/handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class KernelActionHandler(APIHandler):
7575
def post(self, kernel_id, action):
7676
km = self.kernel_manager
7777
if action == 'interrupt':
78-
km.interrupt_kernel(kernel_id)
78+
yield maybe_future(km.interrupt_kernel(kernel_id))
7979
self.set_status(204)
8080
if action == 'restart':
8181

notebook/services/kernels/kernelmanager.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,7 @@ def _handle_kernel_died(self, kernel_id):
401401
self.log.warning("Kernel %s died, removing from map.", kernel_id)
402402
self.remove_kernel(kernel_id)
403403

404-
@gen.coroutine
405-
def start_kernel(self, kernel_id=None, path=None, **kwargs):
404+
async def start_kernel(self, kernel_id=None, path=None, **kwargs):
406405
"""Start a kernel for a session and return its kernel_id.
407406
408407
Parameters
@@ -421,7 +420,7 @@ def start_kernel(self, kernel_id=None, path=None, **kwargs):
421420
if kernel_id is None:
422421
if path is not None:
423422
kwargs['cwd'] = self.cwd_for_path(path)
424-
kernel_id = yield super(AsyncMappingKernelManager, self).start_kernel(**kwargs)
423+
kernel_id = await super(AsyncMappingKernelManager, self).start_kernel(**kwargs)
425424

426425
self._kernel_connections[kernel_id] = 0
427426
self.start_watching_activity(kernel_id)
@@ -443,11 +442,9 @@ def start_kernel(self, kernel_id=None, path=None, **kwargs):
443442
self._check_kernel_id(kernel_id)
444443
self.log.info("Using existing kernel: %s" % kernel_id)
445444

446-
# py2-compat
447-
raise gen.Return(kernel_id)
445+
return kernel_id
448446

449-
@gen.coroutine
450-
def shutdown_kernel(self, kernel_id, now=False, restart=False):
447+
async def shutdown_kernel(self, kernel_id, now=False, restart=False):
451448
"""Shutdown a kernel by kernel_id"""
452449
self._check_kernel_id(kernel_id)
453450
kernel = self._kernels[kernel_id]
@@ -464,13 +461,12 @@ def shutdown_kernel(self, kernel_id, now=False, restart=False):
464461
type=self._kernels[kernel_id].kernel_name
465462
).dec()
466463

467-
yield super(AsyncMappingKernelManager, self).shutdown_kernel(kernel_id, now=now, restart=restart)
464+
await super(AsyncMappingKernelManager, self).shutdown_kernel(kernel_id, now=now, restart=restart)
468465

469-
@gen.coroutine
470-
def restart_kernel(self, kernel_id, now=False):
466+
async def restart_kernel(self, kernel_id, now=False):
471467
"""Restart a kernel by kernel_id"""
472468
self._check_kernel_id(kernel_id)
473-
yield super(AsyncMappingKernelManager, self).restart_kernel(kernel_id, now=now)
469+
await super(AsyncMappingKernelManager, self).restart_kernel(kernel_id, now=now)
474470
kernel = self.get_kernel(kernel_id)
475471
# return a Future that will resolve when the kernel has successfully restarted
476472
channel = kernel.connect_shell()
@@ -506,7 +502,7 @@ def on_restart_failed():
506502
channel.on_recv(on_reply)
507503
loop = IOLoop.current()
508504
timeout = loop.add_timeout(loop.time() + self.kernel_info_timeout, on_timeout)
509-
raise gen.Return(future)
505+
return future
510506

511507
def kernel_model(self, kernel_id):
512508
"""Return a JSON-safe dict representing a kernel

notebook/services/sessions/tests/test_sessions_api.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@
99
import shutil
1010
import time
1111

12-
pjoin = os.path.join
12+
from unittest import SkipTest
1313

1414
from notebook.utils import url_path_join
1515
from notebook.tests.launchnotebook import NotebookTestBase, assert_http_error
1616
from nbformat.v4 import new_notebook
1717
from nbformat import write
1818

19+
try:
20+
from jupyter_client import AsyncMultiKernelManager
21+
async_testing_enabled = True
22+
except ImportError:
23+
async_testing_enabled = False
24+
25+
pjoin = os.path.join
26+
27+
1928
class SessionAPI(object):
2029
"""Wrapper for notebook API calls."""
2130
def __init__(self, request):
@@ -77,6 +86,7 @@ def modify_kernel_id(self, id, kernel_id):
7786
def delete(self, id):
7887
return self._req('DELETE', id)
7988

89+
8090
class SessionAPITest(NotebookTestBase):
8191
"""Test the sessions web service API"""
8292
def setUp(self):
@@ -254,3 +264,24 @@ def test_modify_kernel_id(self):
254264
kernel.pop('last_activity')
255265
[ k.pop('last_activity') for k in kernel_list ]
256266
self.assertEqual(kernel_list, [kernel])
267+
268+
269+
class AsyncSessionAPITest(SessionAPITest):
270+
"""Test the sessions web service API using the AsyncMappingKernelManager"""
271+
272+
@classmethod
273+
def get_argv(cls):
274+
argv = super(AsyncSessionAPITest, cls).get_argv()
275+
276+
# before we extend the argv with the class, ensure that appropriate jupyter_client is available.
277+
# if not available, don't set kernel_manager_class, resulting in the repeat of sync-based tests.
278+
if async_testing_enabled:
279+
argv.extend(['--NotebookApp.kernel_manager_class='
280+
'notebook.services.kernels.kernelmanager.AsyncMappingKernelManager'])
281+
return argv
282+
283+
def setUp(self):
284+
if not async_testing_enabled:
285+
raise SkipTest("AsyncSessionAPITest.{test_method} skipped due to down-level jupyter_client!".
286+
format(test_method=self._testMethodName))
287+
super(AsyncSessionAPITest, self).setUp()

0 commit comments

Comments
 (0)