Skip to content

Commit 7ed3379

Browse files
committed
Add comment explaining choice of heuristic
1 parent 03f3fad commit 7ed3379

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

jupyter_client/ioloop/restarter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ async def poll(self):
8585
await self.kernel_manager.restart_kernel(now=True, newports=newports)
8686
self._restarting = True
8787
else:
88+
# Since `is_alive` only tests that the kernel process is alive, it does not
89+
# indicate that the kernel has successfully completed startup. To solve this
90+
# correctly, we would need to wait for a kernel info reply, but it is not
91+
# necessarily appropriate to start a kernel client + channels in the
92+
# restarter. Therefore, we use "has been alive continuously for X time" as a
93+
# heuristic for a stable start up.
94+
# See https://github.com/jupyter/jupyter_client/pull/717 for details.
8895
if self._initial_startup and self._last_dead - now >= self.stable_start_time:
8996
self._initial_startup = False
9097
if self._restarting and self._last_dead - now >= self.stable_start_time:

jupyter_client/restarter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ def poll(self):
143143
self.kernel_manager.restart_kernel(now=True, newports=newports)
144144
self._restarting = True
145145
else:
146+
# Since `is_alive` only tests that the kernel process is alive, it does not
147+
# indicate that the kernel has successfully completed startup. To solve this
148+
# correctly, we would need to wait for a kernel info reply, but it is not
149+
# necessarily appropriate to start a kernel client + channels in the
150+
# restarter. Therefore, we use "has been alive continuously for X time" as a
151+
# heuristic for a stable start up.
152+
# See https://github.com/jupyter/jupyter_client/pull/717 for details.
146153
if self._initial_startup and self._last_dead - now >= self.stable_start_time:
147154
self._initial_startup = False
148155
if self._restarting and self._last_dead - now >= self.stable_start_time:

jupyter_client/tests/test_restarter.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def debug_logging():
8080

8181

8282
@pytest.mark.asyncio
83-
async def test_restart_check(config, install_kernel):
83+
async def test_restart_check(config, install_kernel, debug_logging):
8484
"""Test that the kernel is restarted and recovers"""
8585
# If this test failes, run it with --log-cli-level=DEBUG to inspect
8686
N_restarts = 1
@@ -116,6 +116,15 @@ def cb():
116116
# Kill without cleanup to simulate crash:
117117
await km.provisioner.kill()
118118
await restarts[i]
119+
# Wait for kill + restart
120+
max_wait = 10.0
121+
waited = 0.0
122+
while waited < max_wait and km.is_alive():
123+
await asyncio.sleep(0.1)
124+
waited += 0.1
125+
while waited < max_wait and not km.is_alive():
126+
await asyncio.sleep(0.1)
127+
waited += 0.1
119128

120129
assert cbs == N_restarts
121130
assert km.is_alive()
@@ -127,7 +136,7 @@ def cb():
127136

128137

129138
@pytest.mark.asyncio
130-
async def test_restarter_gives_up(config, install_fail_kernel):
139+
async def test_restarter_gives_up(config, install_fail_kernel, debug_logging):
131140
"""Test that the restarter gives up after reaching the restart limit"""
132141
# If this test failes, run it with --log-cli-level=DEBUG to inspect
133142
N_restarts = 1
@@ -173,7 +182,7 @@ def on_death():
173182

174183

175184
@pytest.mark.asyncio
176-
async def test_async_restart_check(config, install_kernel):
185+
async def test_async_restart_check(config, install_kernel, debug_logging):
177186
"""Test that the kernel is restarted and recovers"""
178187
# If this test failes, run it with --log-cli-level=DEBUG to inspect
179188
N_restarts = 1
@@ -209,6 +218,15 @@ def cb():
209218
# Kill without cleanup to simulate crash:
210219
await km.provisioner.kill()
211220
await restarts[i]
221+
# Wait for kill + restart
222+
max_wait = 10.0
223+
waited = 0.0
224+
while waited < max_wait and await km.is_alive():
225+
await asyncio.sleep(0.1)
226+
waited += 0.1
227+
while waited < max_wait and not await km.is_alive():
228+
await asyncio.sleep(0.1)
229+
waited += 0.1
212230

213231
assert cbs == N_restarts
214232
assert await km.is_alive()
@@ -220,7 +238,7 @@ def cb():
220238

221239

222240
@pytest.mark.asyncio
223-
async def test_async_restarter_gives_up(config, install_slow_fail_kernel):
241+
async def test_async_restarter_gives_up(config, install_slow_fail_kernel, debug_logging):
224242
"""Test that the restarter gives up after reaching the restart limit"""
225243
# If this test failes, run it with --log-cli-level=DEBUG to inspect
226244
N_restarts = 2

0 commit comments

Comments
 (0)