Skip to content

Commit 6663fae

Browse files
committed
Change run_blocking into run_sync
1 parent 7bddf0a commit 6663fae

File tree

4 files changed

+60
-50
lines changed

4 files changed

+60
-50
lines changed

nbclient/client.py

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from nbformat.v4 import output_from_msg
1717

1818
from .exceptions import CellTimeoutError, DeadKernelError, CellExecutionComplete, CellExecutionError
19+
from .util import run_sync
1920

2021

2122
def timestamp():
@@ -294,43 +295,6 @@ def __init__(self, nb, km=None, **kw):
294295
self.km = km
295296
self.reset_execution_trackers()
296297

297-
def run_blocking(self, coro):
298-
"""Runs a coroutine and blocks until it has executed.
299-
300-
An event loop is created if no one already exists. If an event loop is
301-
already running, this event loop execution is nested into the already
302-
running one if `nest_asyncio` is set to True.
303-
304-
Parameters
305-
----------
306-
coro : coroutine
307-
The coroutine to be executed.
308-
309-
Returns
310-
-------
311-
result :
312-
Whatever the coroutine returns.
313-
"""
314-
try:
315-
loop = asyncio.get_event_loop()
316-
except RuntimeError:
317-
loop = asyncio.new_event_loop()
318-
asyncio.set_event_loop(loop)
319-
if self.nest_asyncio:
320-
import nest_asyncio
321-
nest_asyncio.apply(loop)
322-
try:
323-
result = loop.run_until_complete(coro)
324-
except RuntimeError as e:
325-
if str(e) == 'This event loop is already running':
326-
raise RuntimeError(
327-
'You are trying to run nbclient in an environment where an '
328-
'event loop is already running. Please pass `nest_asyncio=True` in '
329-
'`NotebookClient.execute` and such methods.'
330-
)
331-
raise
332-
return result
333-
334298
def reset_execution_trackers(self):
335299
"""Resets any per-execution trackers.
336300
"""
@@ -417,9 +381,6 @@ async def setup_kernel(self, **kwargs):
417381
self.kc.stop_channels()
418382
self.kc = None
419383

420-
def execute(self, **kwargs):
421-
return self.run_blocking(self.async_execute(**kwargs))
422-
423384
async def async_execute(self, **kwargs):
424385
"""
425386
Executes each code cell.
@@ -445,6 +406,8 @@ async def async_execute(self, **kwargs):
445406

446407
return self.nb
447408

409+
execute = run_sync(async_execute)
410+
448411
def set_widgets_metadata(self):
449412
if self.widget_state:
450413
self.nb.metadata.widgets = {
@@ -511,7 +474,7 @@ async def _poll_for_reply(self, msg_id, cell, timeout, task_poll_output_msg):
511474
timeout = max(0, deadline - monotonic())
512475
except Empty:
513476
# received no message, check if kernel is still alive
514-
self._check_alive()
477+
await self._check_alive()
515478
self._handle_timeout(timeout, cell)
516479

517480
async def _poll_output_msg(self, parent_msg_id, cell, cell_index):
@@ -545,8 +508,8 @@ def _handle_timeout(self, timeout, cell=None):
545508
"Cell execution timed out", timeout, cell
546509
)
547510

548-
def _check_alive(self):
549-
if not self.kc.is_alive():
511+
async def _check_alive(self):
512+
if not await self.kc.is_alive():
550513
self.log.error("Kernel died while waiting for execute reply.")
551514
raise DeadKernelError("Kernel died")
552515

@@ -559,7 +522,7 @@ async def _wait_for_reply(self, msg_id, cell=None):
559522
try:
560523
msg = await self.kc.shell_channel.get_msg(timeout=self.shell_timeout_interval)
561524
except Empty:
562-
self._check_alive()
525+
await self._check_alive()
563526
cummulative_time += self.shell_timeout_interval
564527
if timeout and cummulative_time > timeout:
565528
self._handle_timeout(timeout, cell)
@@ -591,11 +554,6 @@ def _check_raise_for_error(self, cell, exec_reply):
591554
if (exec_reply is not None) and exec_reply['content']['status'] == 'error':
592555
raise CellExecutionError.from_cell_and_msg(cell, exec_reply['content'])
593556

594-
def execute_cell(self, cell, cell_index, execution_count=None, store_history=True):
595-
return self.run_blocking(
596-
self.async_execute_cell(cell, cell_index, execution_count, store_history)
597-
)
598-
599557
async def async_execute_cell(self, cell, cell_index, execution_count=None, store_history=True):
600558
"""
601559
Executes a single code cell.
@@ -661,6 +619,8 @@ async def async_execute_cell(self, cell, cell_index, execution_count=None, store
661619
self.nb['cells'][cell_index] = cell
662620
return cell
663621

622+
execute_cell = run_sync(async_execute_cell)
623+
664624
def process_message(self, msg, cell, cell_index):
665625
"""
666626
Processes a kernel message, updates cell state, and returns the

nbclient/tests/test_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ def test_busy_message(self, executor, cell_mock, message_mock):
672672
)
673673
def test_deadline_exec_reply(self, executor, cell_mock, message_mock):
674674
# exec_reply is never received, so we expect to hit the timeout.
675+
async def is_alive():
676+
return True
677+
executor.kc.is_alive = is_alive
675678
executor.kc.shell_channel.get_msg = MagicMock(side_effect=Empty())
676679
executor.timeout = 1
677680

nbclient/util.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""General utility methods"""
2+
3+
# Copyright (c) Jupyter Development Team.
4+
# Distributed under the terms of the Modified BSD License.
5+
6+
import asyncio
7+
8+
9+
def run_sync(coro):
10+
"""Runs a coroutine and blocks until it has executed.
11+
12+
An event loop is created if no one already exists. If an event loop is
13+
already running, this event loop execution is nested into the already
14+
running one if `nest_asyncio` is set to True.
15+
16+
Parameters
17+
----------
18+
coro : coroutine
19+
The coroutine to be executed.
20+
21+
Returns
22+
-------
23+
result :
24+
Whatever the coroutine returns.
25+
"""
26+
def wrapped(self, *args, **kwargs):
27+
try:
28+
loop = asyncio.get_event_loop()
29+
except RuntimeError:
30+
loop = asyncio.new_event_loop()
31+
asyncio.set_event_loop(loop)
32+
if self.nest_asyncio:
33+
import nest_asyncio
34+
nest_asyncio.apply(loop)
35+
try:
36+
result = loop.run_until_complete(coro(self, *args, **kwargs))
37+
except RuntimeError as e:
38+
if str(e) == 'This event loop is already running':
39+
raise RuntimeError(
40+
'You are trying to run nbclient in an environment where an '
41+
'event loop is already running. Please pass `nest_asyncio=True` in '
42+
'`NotebookClient.execute` and such methods.'
43+
)
44+
raise
45+
return result
46+
wrapped.__doc__ = coro.__doc__
47+
return wrapped

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
traitlets>=4.2
2-
jupyter_client>=6.0.0
2+
jupyter_client @ git+https://github.com/jupyter/jupyter_client
33
nbformat>=5.0
44
async_generator
55
nest_asyncio

0 commit comments

Comments
 (0)