Skip to content

Commit

Permalink
restart kernel daemon when non-package Python modules change
Browse files Browse the repository at this point in the history
  • Loading branch information
jjallaire committed Oct 28, 2023
1 parent b71ffa0 commit 230ba9b
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 7 deletions.
1 change: 1 addition & 0 deletions news/changelog-1.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
- Only search for Julia conda installation when the engine language is Julia
- Support for `plotly-connected` option to determine where Plotly is embedded or loaded from CDN
- Reduce default margins for Plotly figures (t=30,r=0,b=0,l=0)
- Restart kernel daemon when non-package Python modules change

## Knitr

Expand Down
27 changes: 27 additions & 0 deletions src/resources/jupyter/lang/python/cleanup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@

# reset state
%reset

import sys
import types
import os
import json

# NOTE: the kernel_deps code is repeated in the setup.py file
# (we can't easily share this code b/c of the way it is run).
# If you edit this code also edit the same code in setup.py!

kernel_deps = dict()
for module in list(sys.modules.values()):
# Some modules play games with sys.modules (e.g. email/__init__.py
# in the standard library), and occasionally this can cause strange
# failures in getattr. Just ignore anything that's not an ordinary
# module.
if not isinstance(module, types.ModuleType):
continue
path = getattr(module, "__file__", None)
if not path:
continue
if path.endswith(".pyc") or path.endswith(".pyo"):
path = path[:-1]
if not os.path.exists(path):
continue
kernel_deps[path] = os.stat(path).st_mtime
print(json.dumps(kernel_deps))
4 changes: 4 additions & 0 deletions src/resources/jupyter/lang/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = interactivity

# NOTE: the kernel_deps code is repeated in the cleanup.py file
# (we can't easily share this code b/c of the way it is run).
# If you edit this code also edit the same code in cleanup.py!

# output kernel dependencies
kernel_deps = dict()
for module in list(sys.modules.values()):
Expand Down
19 changes: 12 additions & 7 deletions src/resources/jupyter/notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,19 +225,16 @@ def notebook_execute(options, status):

# if this was the setup cell, see if we need to exit b/c dependencies are out of date
if index == 0:
kernel_deps = nb_kernel_depenencies(cell)
if kernel_deps:
if hasattr(notebook_execute, "kernel_deps"):
# confirm kernel_deps haven't changed (restart if they have)
if hasattr(notebook_execute, "kernel_deps"):
kernel_deps = nb_kernel_depenencies(cell)
if kernel_deps:
for path in kernel_deps.keys():
if path in notebook_execute.kernel_deps.keys():
if notebook_execute.kernel_deps[path] != kernel_deps[path]:
raise RestartKernel
else:
notebook_execute.kernel_deps[path] = kernel_deps[path]
else:
notebook_execute.kernel_deps = kernel_deps
else:
notebook_execute.kernel_deps = {}

# we are done w/ setup (with no restarts) so it's safe to print 'Executing...'
if not quiet:
Expand Down Expand Up @@ -284,6 +281,14 @@ def notebook_execute(options, status):
)
nb.cells.pop()

# record kernel deps after execution (picks up imports that occurred
# witihn the notebook cells)
kernel_deps = nb_kernel_depenencies(cleanup_cell)
if kernel_deps:
notebook_execute.kernel_deps = kernel_deps
else:
notebook_execute.kernel_deps = {}

# progress
if not quiet:
status("\n")
Expand Down

0 comments on commit 230ba9b

Please sign in to comment.