conkernelclient 0.0.8__tar.gz → 0.0.10__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.10
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.10"
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',
@@ -17,4 +21,5 @@ d = { 'settings': { 'branch': 'main',
17
21
  'conkernelclient.core.ConKernelClient.stop_channels': ( 'core.html#conkernelclient.stop_channels',
18
22
  'conkernelclient/core.py'),
19
23
  'conkernelclient.core.ConKernelManager': ('core.html#conkernelmanager', 'conkernelclient/core.py'),
24
+ 'conkernelclient.core.DeadKernelError': ('core.html#deadkernelerror', 'conkernelclient/core.py'),
20
25
  'conkernelclient.core._send': ('core.html#_send', 'conkernelclient/core.py')}}}
@@ -3,7 +3,7 @@
3
3
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
4
4
 
5
5
  # %% auto #0
6
- __all__ = ['ConKernelClient', 'ConKernelManager']
6
+ __all__ = ['DeadKernelError', 'ConKernelClient', 'ConKernelManager']
7
7
 
8
8
  # %% ../nbs/00_core.ipynb #d3b521c0
9
9
  from queue import Empty
@@ -15,7 +15,13 @@ 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__)
22
+
23
+ # %% ../nbs/00_core.ipynb #a093006c
24
+ class DeadKernelError(Exception): pass
19
25
 
20
26
  # %% ../nbs/00_core.ipynb #374b75d0
21
27
  if not hasattr(Session, '_orig_send'): Session._orig_send = Session.send
@@ -36,6 +42,17 @@ Session.send = _send
36
42
 
37
43
  # %% ../nbs/00_core.ipynb #d6a5fa6a
38
44
  class ConKernelClient(AsyncKernelClient):
45
+ def _fail_pending(self, exc:Exception, skip=None):
46
+ for k,(q,_) in list(getattr(self, '_pending', {}).items()):
47
+ if k != skip:
48
+ try: q.put_nowait(exc)
49
+ except asyncio.QueueFull: pass
50
+
51
+ def _check_alive(self):
52
+ if not self.channels_running: raise RuntimeError("Channels not running")
53
+ tk = getattr(self, '_shell_reader_task', None)
54
+ return tk is not None and not tk.done()
55
+
39
56
  async def start_channels(self, shell:bool=True, iopub:bool=True, stdin:bool=True, hb:bool=True, control:bool=True):
40
57
  "Start channels, wait for ready, and launch background shell-reply reader"
41
58
  super().start_channels(shell=shell, iopub=iopub, stdin=stdin, hb=hb, control=control)
@@ -47,12 +64,21 @@ class ConKernelClient(AsyncKernelClient):
47
64
  while True:
48
65
  try: reply = await self.get_shell_msg(timeout=None)
49
66
  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}")
67
+ if isinstance(e, (AssertionError, OSError, ZMQError)): e = DeadKernelError(f"Kernel socket closed: {e}")
68
+ self._fail_pending(e)
69
+ _log.warning(f"_reader died, pending={list(self._pending)}: {e}")
53
70
  break
54
- q = self._pending.get(reply["parent_header"].get("msg_id"))
55
- if q: await q.put(reply)
71
+ mid = reply["parent_header"].get("msg_id")
72
+ pend = self._pending.get(mid)
73
+ if pend:
74
+ q, soe = pend
75
+ try: q.put_nowait(reply)
76
+ except asyncio.QueueFull: pass
77
+ else: _log.warning(f"Orphan reply for {reply['parent_header'].get('msg_id')}, pending={list(self._pending)}")
78
+ cts = reply.get("content", {})
79
+ if cts.get("status") in ("error", "aborted") and pend and soe:
80
+ exc = RuntimeError(f"Kernel error aborted: {cts.get('ename')}: {cts.get('evalue')}")
81
+ self._fail_pending(exc, skip=mid)
56
82
  self._shell_reader_task = asyncio.create_task(_reader())
57
83
  await _ready.wait()
58
84
  await asyncio.sleep(0.2)
@@ -60,6 +86,7 @@ class ConKernelClient(AsyncKernelClient):
60
86
 
61
87
  def stop_channels(self):
62
88
  "Stop channels and cancel the background shell-reply reader task"
89
+ self._fail_pending(RuntimeError("Shell channels stopped before reply"))
63
90
  super().stop_channels()
64
91
  if (tk := getattr(self, '_shell_reader_task', None)):
65
92
  tk.cancel()
@@ -68,33 +95,35 @@ class ConKernelClient(AsyncKernelClient):
68
95
 
69
96
  async def _async_recv_reply(self, msg_id, timeout=None, channel="shell"):
70
97
  if channel == "control": return await self._async_get_control_msg(timeout=timeout)
71
- q = self._pending[msg_id]
98
+ q, _ = self._pending[msg_id]
72
99
  try:
73
100
  res = await asyncio.wait_for(q.get(), timeout)
74
101
  if isinstance(res, Exception): raise res
75
102
  return res
76
- except asyncio.TimeoutError as e: raise TimeoutError("Timeout waiting for reply") from e
77
103
  finally: self._pending.pop(msg_id, None)
78
104
 
79
105
  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):
106
+ cts_typ='code', timeout=60, msg_id=None, stop_on_error=True, **kw):
81
107
  "Send an execute request, returning a coroutine for the reply if `reply`, else the msg_id"
108
+ if not self._check_alive(): return asyncio.sleep(0) if reply else None
82
109
  if user_expressions is None: user_expressions = {}
83
110
  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)
111
+ content = dict(user_expressions=user_expressions, allow_stdin=allow_stdin, subsh_id=subsh_id, stop_on_error=stop_on_error, **kw)
85
112
  content[cts_typ] = code
86
113
  msg = self.session.msg("execute_request", content)
87
114
  if msg_id is not None: msg["header"]["msg_id"] = msg_id
88
115
  if subsh_id is not None: msg["header"]["subshell_id"] = subsh_id
89
116
  msg_id = msg["header"]["msg_id"]
90
- if reply: self._pending[msg_id] = asyncio.Queue(maxsize=1)
91
- self.shell_channel.send(msg)
117
+ if reply: self._pending[msg_id] = (asyncio.Queue(maxsize=1), stop_on_error)
118
+ try: self.shell_channel.send(msg)
119
+ except (AssertionError, OSError, ZMQError) as e: raise DeadKernelError(f"Kernel socket closed: {e}") from e
92
120
  if not reply: return msg_id
93
121
  return self._async_recv_reply(msg_id, timeout=timeout)
94
122
 
95
123
  async def stdin_send(self, msg:dict):
96
124
  "Send on `stdin_channel` then ping event loop"
97
- self.stdin_channel.send(msg)
125
+ try: self.stdin_channel.send(msg)
126
+ except (AssertionError, OSError, ZMQError) as e: raise DeadKernelError(f"Kernel socket closed: {e}") from e
98
127
  await asyncio.sleep(0.01)
99
128
 
100
129
  # %% ../nbs/00_core.ipynb #b828c222
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conkernelclient
3
- Version: 0.0.8
3
+ Version: 0.0.10
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 *