conkernelclient 0.0.8__tar.gz → 0.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conkernelclient
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Concurrent-safe Jupyter KernelClient
5
5
  Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
6
6
  License: Apache-2.0
@@ -0,0 +1,2 @@
1
+ __version__ = "0.0.9"
2
+ from .core import *
@@ -8,6 +8,10 @@ d = { 'settings': { 'branch': 'main',
8
8
  'syms': { 'conkernelclient.core': { 'conkernelclient.core.ConKernelClient': ('core.html#conkernelclient', 'conkernelclient/core.py'),
9
9
  'conkernelclient.core.ConKernelClient._async_recv_reply': ( 'core.html#conkernelclient._async_recv_reply',
10
10
  'conkernelclient/core.py'),
11
+ 'conkernelclient.core.ConKernelClient._check_alive': ( 'core.html#conkernelclient._check_alive',
12
+ 'conkernelclient/core.py'),
13
+ 'conkernelclient.core.ConKernelClient._fail_pending': ( 'core.html#conkernelclient._fail_pending',
14
+ 'conkernelclient/core.py'),
11
15
  'conkernelclient.core.ConKernelClient.execute': ( 'core.html#conkernelclient.execute',
12
16
  'conkernelclient/core.py'),
13
17
  'conkernelclient.core.ConKernelClient.start_channels': ( 'core.html#conkernelclient.start_channels',
@@ -15,7 +15,10 @@ from zmq.error import ZMQError
15
15
  from jupyter_client.kernelspec import KernelSpec
16
16
  from jupyter_client import AsyncKernelManager
17
17
  from traitlets import Type
18
- import asyncio, zmq.asyncio, time
18
+ import asyncio, zmq.asyncio, time, logging
19
+
20
+ # %% ../nbs/00_core.ipynb #737a0fc1
21
+ _log = logging.getLogger(__name__)
19
22
 
20
23
  # %% ../nbs/00_core.ipynb #374b75d0
21
24
  if not hasattr(Session, '_orig_send'): Session._orig_send = Session.send
@@ -36,6 +39,17 @@ Session.send = _send
36
39
 
37
40
  # %% ../nbs/00_core.ipynb #d6a5fa6a
38
41
  class ConKernelClient(AsyncKernelClient):
42
+ def _fail_pending(self, exc:Exception, skip=None):
43
+ for k,(q,_) in list(getattr(self, '_pending', {}).items()):
44
+ if k != skip:
45
+ try: q.put_nowait(exc)
46
+ except asyncio.QueueFull: pass
47
+
48
+ def _check_alive(self):
49
+ if not self.channels_running: raise RuntimeError("Channels not running")
50
+ tk = getattr(self, '_shell_reader_task', None)
51
+ return tk is not None and not tk.done()
52
+
39
53
  async def start_channels(self, shell:bool=True, iopub:bool=True, stdin:bool=True, hb:bool=True, control:bool=True):
40
54
  "Start channels, wait for ready, and launch background shell-reply reader"
41
55
  super().start_channels(shell=shell, iopub=iopub, stdin=stdin, hb=hb, control=control)
@@ -47,12 +61,20 @@ class ConKernelClient(AsyncKernelClient):
47
61
  while True:
48
62
  try: reply = await self.get_shell_msg(timeout=None)
49
63
  except Exception as e:
50
- for q in self._pending.values(): await q.put(e)
51
- if self._pending: logging.warning(f"_reader died with pending - {self._pending}: {e}")
52
- else: logging.warning(f"_reader died with no pending: {e}")
64
+ self._fail_pending(e)
65
+ _log.warning(f"_reader died, pending={list(self._pending)}: {e}")
53
66
  break
54
- q = self._pending.get(reply["parent_header"].get("msg_id"))
55
- if q: await q.put(reply)
67
+ mid = reply["parent_header"].get("msg_id")
68
+ pend = self._pending.get(mid)
69
+ if pend:
70
+ q, soe = pend
71
+ try: q.put_nowait(reply)
72
+ except asyncio.QueueFull: pass
73
+ else: _log.warning(f"Orphan reply for {reply['parent_header'].get('msg_id')}, pending={list(self._pending)}")
74
+ cts = reply.get("content", {})
75
+ if cts.get("status") in ("error", "aborted") and pend and soe:
76
+ exc = RuntimeError(f"Kernel error aborted: {cts.get('ename')}: {cts.get('evalue')}")
77
+ self._fail_pending(exc, skip=mid)
56
78
  self._shell_reader_task = asyncio.create_task(_reader())
57
79
  await _ready.wait()
58
80
  await asyncio.sleep(0.2)
@@ -60,6 +82,7 @@ class ConKernelClient(AsyncKernelClient):
60
82
 
61
83
  def stop_channels(self):
62
84
  "Stop channels and cancel the background shell-reply reader task"
85
+ self._fail_pending(RuntimeError("Shell channels stopped before reply"))
63
86
  super().stop_channels()
64
87
  if (tk := getattr(self, '_shell_reader_task', None)):
65
88
  tk.cancel()
@@ -68,26 +91,26 @@ class ConKernelClient(AsyncKernelClient):
68
91
 
69
92
  async def _async_recv_reply(self, msg_id, timeout=None, channel="shell"):
70
93
  if channel == "control": return await self._async_get_control_msg(timeout=timeout)
71
- q = self._pending[msg_id]
94
+ q, _ = self._pending[msg_id]
72
95
  try:
73
96
  res = await asyncio.wait_for(q.get(), timeout)
74
97
  if isinstance(res, Exception): raise res
75
98
  return res
76
- except asyncio.TimeoutError as e: raise TimeoutError("Timeout waiting for reply") from e
77
99
  finally: self._pending.pop(msg_id, None)
78
100
 
79
101
  def execute(self, code, user_expressions=None, allow_stdin=None, reply=False, subsh_id=None,
80
- cts_typ='code', timeout=60, msg_id=None, **kw):
102
+ cts_typ='code', timeout=60, msg_id=None, stop_on_error=True, **kw):
81
103
  "Send an execute request, returning a coroutine for the reply if `reply`, else the msg_id"
104
+ if not self._check_alive(): return asyncio.sleep(0) if reply else None
82
105
  if user_expressions is None: user_expressions = {}
83
106
  if allow_stdin is None: allow_stdin = self.allow_stdin
84
- content = dict(user_expressions=user_expressions, allow_stdin=allow_stdin, subsh_id=subsh_id, **kw)
107
+ content = dict(user_expressions=user_expressions, allow_stdin=allow_stdin, subsh_id=subsh_id, stop_on_error=stop_on_error, **kw)
85
108
  content[cts_typ] = code
86
109
  msg = self.session.msg("execute_request", content)
87
110
  if msg_id is not None: msg["header"]["msg_id"] = msg_id
88
111
  if subsh_id is not None: msg["header"]["subshell_id"] = subsh_id
89
112
  msg_id = msg["header"]["msg_id"]
90
- if reply: self._pending[msg_id] = asyncio.Queue(maxsize=1)
113
+ if reply: self._pending[msg_id] = (asyncio.Queue(maxsize=1), stop_on_error)
91
114
  self.shell_channel.send(msg)
92
115
  if not reply: return msg_id
93
116
  return self._async_recv_reply(msg_id, timeout=timeout)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conkernelclient
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Concurrent-safe Jupyter KernelClient
5
5
  Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
6
6
  License: Apache-2.0
@@ -39,3 +39,5 @@ version = {attr = "conkernelclient.__version__"}
39
39
  include = ["conkernelclient"]
40
40
 
41
41
  [tool.nbdev]
42
+ allowed_metadata_keys = ['solveit_dialog_mode', 'solveit_ver']
43
+ allowed_cell_metadata_keys = ["solveit_ai"]
@@ -1,2 +0,0 @@
1
- __version__ = "0.0.8"
2
- from .core import *
File without changes