67 from nbformat
import v3, v4
68 from datetime
import datetime, date
74 gTypesList = [
"void",
"int",
"Int_t",
"TF1",
"string",
"bool",
"double",
"float",
"char",
75 "TCanvas",
"TTree",
"TString",
"TSeqCollection",
"Double_t",
"TFile",
"Long64_t",
"Bool_t",
"TH1",
76 "RooDataSet",
"RooWorkspace" ,
"HypoTestInverterResult" ,
"TVectorD" ,
"TArrayF",
"UInt_t"]
84 Returns string with each line unindented by 3 spaces. If line isn't indented, it stays the same.
85 >>> unindenter(" foobar")
87 >>> unindenter("foobar")
89 >>> unindenter('''foobar
92 'foobar\\nfoobar\\nfoobar\\n'
95 lines = string.splitlines()
97 if line.startswith(spaces*
' '):
98 newstring += (line[spaces:] +
"\n")
100 newstring += (line +
"\n")
107 Extract author and description from header, eliminate header from text. Also returns
108 notebook boolean, which is True if the string \notebook is present in the header
109 Also determine options (-js, -nodraw, -header) passed in \notebook command, and
110 return their booleans
111 >>> readHeaderPython('''## \\file
112 ... ## \\ingroup tutorials
114 ... ## This is the description of the tutorial
119 ... ## \\\\author John Brown
120 ... def tutorialfuncion()''')
121 ('def tutorialfuncion()\\n', 'This is the description of the tutorial\\n\\n\\n', 'John Brown', True, False, False, False)
122 >>> readHeaderPython('''## \\file
123 ... ## \\ingroup tutorials
124 ... ## \\\\notebook -js
125 ... ## This is the description of the tutorial
130 ... ## \\\\author John Brown
131 ... def tutorialfuncion()''')
132 ('def tutorialfuncion()\\n', 'This is the description of the tutorial\\n\\n\\n', 'John Brown', True, True, False, False)
133 >>> readHeaderPython('''## \\file
134 ... ## \\ingroup tutorials
135 ... ## \\\\notebook -nodraw
136 ... ## This is the description of the tutorial
141 ... ## \\\\author John Brown
142 ... def tutorialfuncion()''')
143 ('def tutorialfuncion()\\n', 'This is the description of the tutorial\\n\\n\\n', 'John Brown', True, False, True, False)
145 lines = text.splitlines()
152 needsHeaderFile =
False
153 for i, line
in enumerate(lines):
154 if line.startswith(
"## \\aut"):
156 elif line.startswith(
"## \\note"):
160 if "-nodraw" in line:
162 if "-header" in line:
163 needsHeaderFile =
True
164 elif line.startswith(
"##"):
165 if not line.startswith(
"## \\")
and isNotebook:
166 description += (line[3:] +
'\n')
170 for line
in lines[i:]:
171 newtext += (line +
"\n")
173 return newtext, description, author, isNotebook, isJsroot, nodraw, needsHeaderFile
178 Converts comments delimited by # or ## and on a new line into a markdown cell.
179 For python files only
180 >>> pythonComments('''## This is a
181 ... ## multiline comment
182 ... def function()''')
183 '# <markdowncell>\\n## This is a\\n## multiline comment\\n# <codecell>\\ndef function()\\n'
184 >>> pythonComments('''def function():
185 ... variable = 5 # Comment not in cell
186 ... # Comment also not in cell''')
187 'def function():\\n variable = 5 # Comment not in cell\\n # Comment also not in cell\\n'
189 text = text.splitlines()
192 for i, line
in enumerate(text):
193 if line.startswith(
"#")
and not inComment:
195 newtext +=
"# <markdowncell>\n"
196 newtext += (line +
"\n")
197 elif inComment
and not line.startswith(
"#"):
199 newtext +=
"# <codecell>\n"
200 newtext += (line+
"\n")
202 newtext += (line+
"\n")
207 lines = text.splitlines()
208 functionContentRe = re.compile(
'def %s\\(.*\\):' % tutName , flags = re.DOTALL | re.MULTILINE)
210 inMainFunction =
False
211 hasMainFunction =
False
215 if line.startswith(
"""if __name__ == "__main__":""")
or line.startswith(
"""if __name__ == '__main__':"""):
217 match = functionContentRe.search(line)
218 if inMainFunction
and not line.startswith(
" ")
and line !=
"":
219 inMainFunction =
False
221 inMainFunction =
True
222 hasMainFunction =
True
225 newtext += (line[4:] +
'\n')
227 newtext += (line +
'\n')
233 Extract author and description from header, eliminate header from text. Also returns
234 notebook boolean, which is True if the string \notebook is present in the header
235 Also determine options (-js, -nodraw, -header) passed in \notebook command, and
236 return their booleans
237 >>> readHeaderCpp('''/// \\file
238 ... /// \\ingroup tutorials
240 ... /// This is the description of the tutorial
242 ... /// \\macro_image
245 ... /// \\\\author John Brown
246 ... void tutorialfuncion(){}''')
247 ('void tutorialfuncion(){}\\n', '# This is the description of the tutorial\\n# \\n# \\n', 'John Brown', True, False, False, False)
248 >>> readHeaderCpp('''/// \\file
249 ... /// \\ingroup tutorials
250 ... /// \\\\notebook -js
251 ... /// This is the description of the tutorial
253 ... /// \\macro_image
256 ... /// \\\\author John Brown
257 ... void tutorialfuncion(){}''')
258 ('void tutorialfuncion(){}\\n', '# This is the description of the tutorial\\n# \\n# \\n', 'John Brown', True, True, False, False)
259 >>> readHeaderCpp('''/// \\file
260 ... /// \\ingroup tutorials
261 ... /// \\\\notebook -nodraw
262 ... /// This is the description of the tutorial
264 ... /// \\macro_image
267 ... /// \\\\author John Brown
268 ... void tutorialfuncion(){}''')
269 ('void tutorialfuncion(){}\\n', '# This is the description of the tutorial\\n# \\n# \\n', 'John Brown', True, False, True, False)
271 lines = text.splitlines()
278 needsHeaderFile =
False
279 for i, line
in enumerate(lines):
280 if line.startswith(
"/// \\aut"):
282 if line.startswith(
"/// \\note"):
286 if "-nodraw" in line:
288 if "-header" in line:
289 needsHeaderFile =
True
290 if line.startswith(
"///"):
291 if not line.startswith(
"/// \\")
and isNotebook:
292 description += (
'# ' + line[4:] +
'\n')
296 for line
in lines[i:]:
297 newtext += (line +
"\n")
298 description = description.replace(
"\\f$",
"$")
299 description = description.replace(
"\\f[",
"$$")
300 description = description.replace(
"\\f]",
"$$")
301 return newtext, description, author, isNotebook, isJsroot, nodraw, needsHeaderFile
306 Extracts main function for the function enclosure by means of regular expression
307 >>> cppFunction('''void mainfunction(arguments = values){
308 ... content of function
312 '\\n content of function\\n which spans\\n several lines\\n'
313 >>> cppFunction('''void mainfunction(arguments = values)
315 ... content of function
319 '\\n content of function\\n which spans\\n several lines\\n'
320 >>> cppFunction('''void mainfunction(arguments = values
321 ... morearguments = morevalues)
323 ... content of function
327 '\\n content of function\\n which spans\\n several lines\\n'
329 functionContentRe = re.compile(
r'(?<=\{).*(?=^\})', flags = re.DOTALL | re.MULTILINE)
331 match = functionContentRe.search(text)
340 Converts comments delimited by // and on a new line into a markdown cell. For C++ files only.
341 >>> cppComments('''// This is a
342 ... // multiline comment
343 ... void function(){}''')
344 '# <markdowncell>\\n# This is a\\n# multiline comment\\n# <codecell>\\nvoid function(){}\\n'
345 >>> cppComments('''void function(){
346 ... int variable = 5 // Comment not in cell
347 ... // Comment also not in cell
349 'void function(){\\n int variable = 5 // Comment not in cell\\n // Comment also not in cell\\n}\\n'
351 text = text.splitlines()
356 if line.startswith(
"//")
and not inComment:
358 newtext +=
"# <markdowncell>\n"
359 if line[2:].lstrip().startswith(
"#"):
360 newtext += (
"# " + line[2:]+
"\n")
362 newtext += (
"# " + line[2:].lstrip().capitalize()+
"\n")
363 elif inComment
and not line.startswith(
"//"):
365 newtext +=
"# <codecell>\n"
366 newtext += (line+
"\n")
367 elif inComment
and line.startswith(
"//"):
368 newtext += (
"# " + line[2:] +
"\n")
370 newtext += (line+
"\n")
377 Splits the text string into main, helpers, and rest. main is the main function,
378 i.e. the function tha thas the same name as the macro file. Helpers is a list of
379 strings, each a helper function, i.e. any other function that is not the main function.
380 Finally, rest is a string containing any top-level code outside of any function.
381 Comments immediately prior to a helper cell are converted into markdown cell,
382 added to the helper, and removed from rest.
383 Intended for C++ files only.
384 >>> split('''void tutorial(){
385 ... content of tutorial
387 ('void tutorial(){\\n content of tutorial\\n}', [], '')
388 >>> split('''void tutorial(){
389 ... content of tutorial
391 ... void helper(arguments = values){
393 ... content spans lines
395 ('void tutorial(){\\n content of tutorial\\n}', ['\\n# <markdowncell>\\n A helper function is created: \\n# <codecell>\\n%%cpp -d\\nvoid helper(arguments = values){\\n helper function\\n content spans lines\\n}'], '')
396 >>> split('''#include <header.h>
397 ... using namespace NAMESPACE
399 ... content of tutorial
401 ... void helper(arguments = values){
403 ... content spans lines
405 ('void tutorial(){\\n content of tutorial\\n}', ['\\n# <markdowncell>\\n A helper function is created: \\n# <codecell>\\n%%cpp -d\\nvoid helper(arguments = values){\\n helper function\\n content spans lines\\n}'], '#include <header.h>\\nusing namespace NAMESPACE')
406 >>> split('''void tutorial(){
407 ... content of tutorial
409 ... // This is a multiline
410 ... // description of the
411 ... // helper function
412 ... void helper(arguments = values){
414 ... content spans lines
416 ('void tutorial(){\\n content of tutorial\\n}', ['\\n# <markdowncell>\\n This is a multiline\\n description of the\\n helper function\\n \\n# <codecell>\\n%%cpp -d\\nvoid helper(arguments = values){\\n helper function\\n content spans lines\\n}'], '')
419 for cpptype
in gTypesList:
420 functionReString += (
"^%s|") % cpptype
422 functionReString = functionReString[:-1] +
r")\s?\*?&?\s?[\w:]*?\s?\([^\)]*\)\s*\{.*?^\}"
424 functionRe = re.compile(functionReString, flags = re.DOTALL | re.MULTILINE)
426 functionMatches = functionRe.finditer(text)
429 for matchString
in [match.group()
for match
in functionMatches]:
433 helpers.append(matchString)
436 rest = text.replace(main,
"")
438 for helper
in helpers:
439 rest = rest.replace(helper,
"")
442 lines = text.splitlines()
443 for helper
in helpers:
444 for i, line
in enumerate(lines):
445 if line.startswith(helper[:helper.find(
"\n")]):
448 while lines[i-j].startswith(
"//"):
449 commentList.append(lines[i-j])
452 commentList.reverse()
453 helperDescription =
''
454 for comment
in commentList:
455 if comment
in (
"//",
"// "):
456 helperDescription +=
"\n\n"
458 helperDescription += (comment[2:] +
"\n")
459 rest = rest.replace(comment,
"")
462 helperDescription =
"A helper function is created:"
466 newHelpers.append(
"\n# <markdowncell>\n " + helperDescription +
" \n# <codecell>\n%%cpp -d\n" + helper)
468 rest = rest.rstrip(
"\n /")
470 return main, newHelpers, rest
475 Takes a string representation of a C++ function as an input,
476 finds and returns the name of the function
477 >>> findFunctionName('void functionName(arguments = values){}')
479 >>> findFunctionName('void functionName (arguments = values){}')
481 >>> findFunctionName('void *functionName(arguments = values){}')
483 >>> findFunctionName('void* functionName(arguments = values){}')
485 >>> findFunctionName('void * functionName(arguments = values){}')
487 >>> findFunctionName('void class::functionName(arguments = values){}')
488 'class::functionName'
490 functionNameReString=
"(?<="
491 for cpptype
in gTypesList:
492 functionNameReString += (
"(?<=%s)|") % cpptype
494 functionNameReString = functionNameReString[:-1] +
r")\s?\*?\s?[^\s]*?(?=\s?\()"
496 functionNameRe = re.compile(functionNameReString, flags = re.DOTALL | re.MULTILINE)
499 match = functionNameRe.search(text)
500 functionname = match.group().strip(
" *\n")
506 Evaluates whether the main function returns a TCanvas or requires input. If it
507 does then the keepfunction flag is True, meaning the function wont be extracted
508 by cppFunction. If the initial condition is true then an extra cell is added
509 before at the end that calls the main function is returned, and added later.
510 >>> processmain('''void function(){
511 ... content of function
515 ('void function(){\\n content of function\\n spanning several\\n lines\\n}', '')
516 >>> processmain('''void function(arguments = values){
517 ... content of function
521 ('void function(arguments = values){\\n content of function\\n spanning several\\n lines\\n}', '# <markdowncell> \\n Arguments are defined. \\n# <codecell>\\narguments = values;\\n# <codecell>\\n')
522 >>> processmain('''void function(argument1 = value1, //comment 1
523 ... argument2 = value2 /*comment 2*/ ,
524 ... argument3 = value3,
525 ... argument4 = value4)
527 ... content of function
531 ('void function(argument1 = value1, //comment 1\\n argument2 = value2 /*comment 2*/ ,\\n argument3 = value3, \\n argument4 = value4)\\n{\\n content of function\\n spanning several\\n lines\\n}', '# <markdowncell> \\n Arguments are defined. \\n# <codecell>\\nargument1 = value1;\\nargument2 = value2;\\nargument3 = value3;\\nargument4 = value4;\\n# <codecell>\\n')
532 >>> processmain('''TCanvas function(){
533 ... content of function
538 ('TCanvas function(){\\n content of function\\n spanning several \\n lines\\n return c1\\n}', '')
544 argumentsre = re.compile(
r'(?<=\().*?(?=\))', flags = re.DOTALL | re.MULTILINE)
545 arguments = argumentsre.search(text)
547 if len(arguments.group()) > 3:
548 argumentsCell =
"# <markdowncell> \n Arguments are defined. \n# <codecell>\n"
549 individualArgumentre = re.compile(
r'[^/\n,]*?=[^/\n,]*')
550 argumentList=individualArgumentre.findall(arguments.group())
551 for argument
in argumentList:
552 argumentsCell += argument.strip(
"\n ") +
";\n"
553 argumentsCell +=
"# <codecell>\n"
555 return text, argumentsCell
559 code = code.replace(
"img->StartPaletteEditor();",
"")
560 code = code.replace(
"Open the color editor",
"")
565 if "copytree" in tutName:
566 return "# <codecell> \n.! $ROOTSYS/test/eventexe 1000 1 1 1 \n" + code
571 if "quasirandom" == tutName:
572 return "# <codecell> \ngSystem->Load(\"libMathMore\"); \n# <codecell> \n" + code
578 def changeString(matchObject):
579 matchString = matchObject.group()
580 matchString = matchString[0] +
" " + matchString[1:]
581 matchString = matchString.replace(
" " ,
"THISISASPACE")
582 matchString = matchString.replace(
" " ,
"")
583 matchString = matchString.replace(
"THISISASPACE" ,
" ")
586 newcode = re.sub(
"#\s\s?\w\s[\w-]\s\w.*", changeString , code)
590 if "using namespace RooFit;\nusing namespace RooStats;" in code:
591 code = code.replace(
"using namespace RooFit;\nusing namespace RooStats;",
"# <codecell>\n%%cpp -d\n// This is a workaround to make sure the namespace is used inside functions\nusing namespace RooFit;\nusing namespace RooStats;\n# <codecell>\n")
594 code = code.replace(
"using namespace RooFit;",
"# <codecell>\n%%cpp -d\n// This is a workaround to make sure the namespace is used inside functions\nusing namespace RooFit;\n# <codecell>\n")
595 code = code.replace(
"using namespace RooStats;",
"# <codecell>\n%%cpp -d\n// This is a workaround to make sure the namespace is used inside functions\nusing namespace RooStats;\n# <codecell>\n")
596 code = code.replace(
"using namespace ROOT::Math;",
"# <codecell>\n%%cpp -d\n// This is a workaround to make sure the namespace is used inside functions\nusing namespace ROOT::Math;\n# <codecell>\n")
602 if tutName ==
"rs401d_FeldmanCousins":
604 """#if !defined(__CINT__) || defined(__MAKECINT__)\n#include "../tutorials/roostats/NuMuToNuE_Oscillation.h"\n#include "../tutorials/roostats/NuMuToNuE_Oscillation.cxx" // so that it can be executed directly\n#else\n#include "../tutorials/roostats/NuMuToNuE_Oscillation.cxx+" // so that it can be executed directly\n#endif""" ,
"""TString tutDir = gROOT->GetTutorialDir();\nTString headerDir = TString::Format("#include \\\"%s/roostats/NuMuToNuE_Oscillation.h\\\"", tutDir.Data());\nTString impDir = TString::Format("#include \\\"%s/roostats/NuMuToNuE_Oscillation.cxx\\\"", tutDir.Data());\ngROOT->ProcessLine(headerDir);\ngROOT->ProcessLine(impDir);""")
609 if tutName !=
"fitcont":
610 code = re.sub(
r"# <codecell>\s*#include",
"# <codecell>\n%%cpp -d\n#include" , code)
615 if tutName ==
"tree4":
617 """#include \"../test/Event.h\"""" ,
"""# <codecell>\nTString dir = "$ROOTSYS/test/Event.h";\ngSystem->ExpandPathName(dir);\nTString includeCommand = TString::Format("#include \\\"%s\\\"" , dir.Data());\ngROOT->ProcessLine(includeCommand);""")
622 code = code.replace(
":DrawProgressBar",
":!DrawProgressBar")
625 codeTransformers=[removePaletteEditor, runEventExe, getLibMathMore,
626 roofitRemoveSpacesComments, declareNamespace, rs401dGetFiles ,
627 declareIncludes, tree4GetFiles, disableDrawProgressBar]
629 for transformer
in codeTransformers:
630 code = transformer(code)
636 code = code.replace(
"~~~" ,
"```")
637 code = code.replace(
"{.cpp}",
"cpp")
638 code = code.replace(
"{.bash}",
"bash")
644 Return True if extension is a C++ file
646 return extension
in (
"C",
"c",
"cpp",
"C++",
"cxx")
650 listLongTutorials = [
"OneSidedFrequentistUpperLimitWithBands",
"StandardBayesianNumericalDemo",
651 "TwoSidedFrequentistUpperLimitWithBands" ,
"HybridStandardForm",
"rs401d_FeldmanCousins",
652 "TMVAMultipleBackgroundExample",
"TMVARegression",
"TMVAClassification",
"StandardHypoTestDemo"]
653 if tutName
in listLongTutorials:
662 Main function. Calls all other functions, depending on whether the macro input is in python or c++.
663 It adds the header information. Also, it adds a cell that draws all canvases. The working text is
664 then converted to a version 3 jupyter notebook, subsequently updated to a version 4. Then, metadata
665 associated with the language the macro is written in is attatched to he notebook. Finally the
666 notebook is executed and output as a Jupyter notebook.
670 main, helpers, rest =
split(text)
675 main = argumentsCell + main
682 text =
"# <markdowncell>\n# The header file must be copied to the current directory\n# <codecell>\n.!cp %s%s.h .\n# <codecell>\n" % (tutRelativePath, tutName)
685 text =
"# <codecell>\n" + rest
687 for helper
in helpers:
690 text += (
"\n# <codecell>\n" + main)
692 if extension ==
"py":
704 text =
"# <markdowncell> \n# # %s\n%s# \n# \n# **Author:** %s \n# <i><small>This notebook tutorial was automatically generated " \
705 "with <a href= \"https://github.com/root-mirror/root/blob/master/documentation/doxygen/converttonotebook.py\">ROOTBOOK-izer (Beta)</a> " \
706 "from the macro found in the ROOT repository on %s.</small></i>\n# <codecell>\n%s" % (tutTitle, newDescription, author, date, text)
709 if isJsroot
and not nodraw:
711 text +=
"\n# <markdowncell> \n# Draw all canvases \n# <codecell>\n%jsroot on\ngROOT->GetListOfCanvases()->Draw()"
712 if extension ==
"py":
713 text +=
"\n# <markdowncell> \n# Draw all canvases \n# <codecell>\n%jsroot on\nfrom ROOT import gROOT \ngROOT.GetListOfCanvases().Draw()"
717 text +=
"\n# <markdowncell> \n# Draw all canvases \n# <codecell>\ngROOT->GetListOfCanvases()->Draw()"
718 if extension ==
"py":
719 text +=
"\n# <markdowncell> \n# Draw all canvases \n# <codecell>\nfrom ROOT import gROOT \ngROOT.GetListOfCanvases().Draw()"
722 nbook = v3.reads_py(text)
723 nbook = v4.upgrade(nbook)
726 json_data = json.loads(v4.writes(nbook))
729 if extension ==
"py":
730 json_data[
u'metadata'] = {
732 "display_name":
"Python 2",
733 "language":
"python",
741 "file_extension":
".py",
742 "mimetype":
"text/x-python",
744 "nbconvert_exporter":
"python",
745 "pygments_lexer":
"ipython2",
750 json_data[
u'metadata'] = {
752 "display_name":
"ROOT C++",
757 "codemirror_mode":
"text/x-c++src",
758 "file_extension":
".C",
759 "mimetype":
" text/x-c++src",
765 with open(outPathName,
'w')
as fout:
766 json.dump(json_data, fout, indent=1, sort_keys=
True)
768 print(time.time() - starttime)
771 r = subprocess.call([
"jupyter",
"nbconvert",
"--ExecutePreprocessor.timeout=%d" % timeout,
"--to=notebook",
"--execute", outPathName])
773 sys.stderr.write(
"NOTEBOOK_CONVERSION_WARNING: Nbconvert failed for notebook %s with return code %s\n" %(outname,r))
776 subprocess.call([
"jupyter",
"trust", os.path.join(outdir, outnameconverted)])
778 os.remove(outPathName)
781 if __name__ ==
"__main__":
783 if str(sys.argv[1]) ==
"-test":
785 doctest.testmod(verbose=
True)
793 tutPathName = str(sys.argv[1])
794 tutPath = os.path.dirname(tutPathName)
795 if tutPath.split(
"/")[-2] ==
"tutorials":
796 tutRelativePath =
"$ROOTSYS/tutorials/%s/" % tutPath.split(
"/")[-1]
797 tutFileName = os.path.basename(tutPathName)
798 tutName, extension = tutFileName.split(
".")
799 tutTitle = re.sub(
r"([A-Z\d])",
r" \1", tutName).
title()
800 outname = tutFileName +
".ipynb"
801 outnameconverted = tutFileName +
".nbconvert.ipynb"
805 outdir = str(sys.argv[2])
809 outPathName = os.path.join(outdir, outname)
811 date = datetime.now().strftime(
"%A, %B %d, %Y at %I:%M %p")
819 os.environ[
"DYLD_LIBRARY_PATH"] = os.environ[
"ROOTSYS"] +
"/lib"
822 with open(tutPathName)
as fin:
826 if extension ==
"py":
827 text, description, author, isNotebook, isJsroot, nodraw, needsHeaderFile =
readHeaderPython(text)
829 text, description, author, isNotebook, isJsroot, nodraw, needsHeaderFile =
readHeaderCpp(text)
832 starttime = time.time()
834 print(time.time() - starttime)
def declareIncludes(code)
def declareNamespace(code)
def disableDrawProgressBar(code)
def removePaletteEditor(code)
def findFunctionName(text)
def pythonMainFunction(text)
def readHeaderPython(text)
def roofitRemoveSpacesComments(code)
def unindenter(string, spaces=3)