Skip to content

Commit 4d812c2

Browse files
committed
[roottest] Use nbconvert as a library in nbdiff.py
Using nbconvert as a Python library is less fragile, because otherwise there is ambiguity how the command line util should be called (the name changed over the versions as it migrated from ipython to jupyter).
1 parent 7c658df commit 4d812c2

File tree

1 file changed

+47
-57
lines changed

1 file changed

+47
-57
lines changed

roottest/python/JupyROOT/nbdiff.py

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,9 @@
22
import json
33
import os
44
import shutil
5-
import subprocess
65
import sys
76
import tempfile
87

9-
nbExtension = ".ipynb"
10-
convCmdTmpl = (
11-
"%s nbconvert "
12-
"--to notebook "
13-
"--ExecutePreprocessor.kernel_name=%s "
14-
"--ExecutePreprocessor.enabled=True "
15-
"--ExecutePreprocessor.timeout=3600 "
16-
"--ExecutePreprocessor.startup_timeout=180 "
17-
"%s "
18-
"--output %s"
19-
)
20-
pythonInterpName = "python3"
21-
22-
rootKernelFileContent = (
23-
"""{
24-
"language": "c++",
25-
"display_name": "ROOT C++",
26-
"argv": [
27-
"%s",
28-
"-m",
29-
"JupyROOT.kernel.rootkernel",
30-
"-f",
31-
"{connection_file}"
32-
]
33-
}
34-
"""
35-
% pythonInterpName
36-
)
37-
388

399
# Replace the criterion according to which a line shall be skipped
4010
def customLineJunkFilter(line):
@@ -134,7 +104,21 @@ def createKernelSpec():
134104
rootKernelPath = os.path.join(kernelsPath, "root")
135105
os.mkdir(rootKernelPath)
136106
with open(os.path.join(rootKernelPath, "kernel.json"), "w") as kernel_file:
137-
kernel_file.write(rootKernelFileContent)
107+
kernel_file.write(
108+
"""{
109+
"language": "c++",
110+
"display_name": "ROOT C++",
111+
"argv": [
112+
"%s",
113+
"-m",
114+
"JupyROOT.kernel.rootkernel",
115+
"-f",
116+
"{connection_file}"
117+
]
118+
}
119+
"""
120+
% sys.executable
121+
)
138122

139123
return tmpd
140124

@@ -149,39 +133,53 @@ def addEtcToEnvironment(inNBDirName):
149133
return ipythondir
150134

151135

152-
def getInterpreterName():
153-
"""Find if the 'jupyter' executable is available on the platform. If
154-
yes, return its name else return 'ipython'
155-
"""
156-
ret = subprocess.call("type jupyter", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
157-
return "jupyter" if ret == 0 else "i%s" % pythonInterpName
158-
159-
160136
def getKernelName(inNBName):
161137
with open(inNBName) as f:
162138
nbj = json.load(f)
163139
if nbj["metadata"]["kernelspec"]["language"] == "python":
164-
return pythonInterpName
140+
return "python3"
165141
else: # we support only Python and C++
166142
return "root"
167143

168144

169-
def canReproduceNotebook(inNBName, kernelName, needsCompare):
145+
def canReproduceNotebook(inNBName, needsCompare):
146+
import nbformat
147+
from nbconvert.preprocessors import ExecutePreprocessor
148+
170149
tmpDir = addEtcToEnvironment(os.path.dirname(inNBName))
171-
outNBName = inNBName.replace(nbExtension, "_out" + nbExtension)
172-
interpName = getInterpreterName()
173-
convCmd = convCmdTmpl % (interpName, kernelName, inNBName, outNBName)
174-
exitStatus = os.system(convCmd) # we use system to inherit the environment in os.environ
175-
shutil.rmtree(tmpDir)
150+
outNBName = inNBName.replace(".ipynb", "_out.ipynb")
151+
152+
# Load input notebook
153+
with open(inNBName, "r", encoding="utf-8") as f:
154+
nb = nbformat.read(f, as_version=4)
155+
156+
# Configure execution
157+
ep = ExecutePreprocessor(
158+
kernel_name=getKernelName(inNBName),
159+
timeout=3600,
160+
startup_timeout=180,
161+
allow_errors=False,
162+
)
163+
164+
# Run the notebook
165+
ep.preprocess(nb, {"metadata": {"path": os.path.dirname(inNBName)}})
166+
167+
# Export executed notebook
168+
with open(outNBName, "w", encoding="utf-8") as f:
169+
nbformat.write(nb, f)
170+
171+
# Compare or return success
176172
if needsCompare:
177173
return compareNotebooks(inNBName, outNBName)
178174
else:
179-
return exitStatus
175+
return 0 # success
176+
177+
shutil.rmtree(tmpDir)
180178

181179

182180
def isInputNotebookFileName(filename):
183181
if not filename.endswith(".ipynb"):
184-
print("Notebook files shall have the %s extension" % nbExtension)
182+
print("Notebook files shall have the .ipynb extension")
185183
return False
186184
return True
187185

@@ -200,13 +198,5 @@ def isInputNotebookFileName(filename):
200198
if not isInputNotebookFileName(nbFileName):
201199
sys.exit(1)
202200

203-
try:
204-
# If jupyter is there, ipython is too
205-
import jupyter
206-
except:
207-
raise ImportError("Cannot import jupyter")
208-
209-
kernelName = getKernelName(nbFileName)
210-
211-
retCode = canReproduceNotebook(nbFileName, kernelName, needsCompare)
201+
retCode = canReproduceNotebook(nbFileName, needsCompare)
212202
sys.exit(retCode)

0 commit comments

Comments
 (0)