ROOT  6.06/08
Reference Guide
utils.py
Go to the documentation of this file.
1 import os
2 import sys
3 import select
4 import tempfile
5 import pty
6 import itertools
7 import re
8 import fnmatch
9 from hashlib import sha1
10 from contextlib import contextmanager
11 from subprocess import check_output
12 from IPython import get_ipython
13 from IPython.display import HTML
14 from IPython.core.extensions import ExtensionManager
15 import IPython.display
16 import ROOT
17 import cpptransformer
18 import cppcompleter
19 
20 
21 # We want iPython to take over the graphics
22 ROOT.gROOT.SetBatch()
23 
24 
25 cppMIME = 'text/x-c++src'
26 ipyMIME = 'text/x-ipython'
27 
28 _jsDefaultHighlight = """
29 // Set default mode for code cells
30 IPython.CodeCell.options_default.cm_config.mode = '{mimeType}';
31 // Set CodeMirror's current mode
32 var cells = IPython.notebook.get_cells();
33 cells[cells.length-1].code_mirror.setOption('mode', '{mimeType}');
34 // Set current mode for newly created cell
35 cells[cells.length-1].cm_config.mode = '{mimeType}';
36 """
37 
38 _jsMagicHighlight = "IPython.CodeCell.config_defaults.highlight_modes['magic_{cppMIME}'] = {{'reg':[/^%%cpp/]}};"
39 
40 
41 _jsNotDrawableClassesPatterns = ["TGraph[23]D","TH3*","TGraphPolar","TProf*","TEve*","TF[23]","TGeo*","TPolyLine3D"]
42 
43 
44 _jsROOTSourceDir = "https://root.cern.ch/js/dev/"
45 _jsCanvasWidth = 800
46 _jsCanvasHeight = 600
47 
48 _jsCode = """
49 <div id="{jsDivId}"
50  style="width: {jsCanvasWidth}px; height: {jsCanvasHeight}px">
51 </div>
52 
53 <script>
54 requirejs.config(
55 {{
56  paths: {{
57  'JSRootCore' : '{jsROOTSourceDir}/scripts/JSRootCore',
58  'JSRootPainter' : '{jsROOTSourceDir}/scripts/JSRootPainter',
59  }}
60 }}
61 );
62 require(['JSRootCore', 'JSRootPainter'],
63  function(Core, Painter) {{
64  var obj = Core.parse('{jsonContent}');
65  Painter.draw("{jsDivId}", obj, "{jsDrawOptions}");
66  }}
67 );
68 </script>
69 """
70 
71 _enableJSVis = False
72 _enableJSVisDebug = False
74  global _enableJSVis
75  _enableJSVis = True
76 
78  global _enableJSVis
79  _enableJSVis = False
80 
82  global _enableJSVis
83  global _enableJSVisDebug
84  _enableJSVis = True
85  _enableJSVisDebug = True
86 
88  global _enableJSVis
89  global _enableJSVisDebug
90  _enableJSVis = False
91  _enableJSVisDebug = False
92 
94  return sys.platform
95 
96 def _getLibExtension(thePlatform):
97  '''Return appropriate file extension for a shared library
98  >>> _getLibExtension('darwin')
99  '.dylib'
100  >>> _getLibExtension('win32')
101  '.dll'
102  >>> _getLibExtension('OddPlatform')
103  '.so'
104  '''
105  pExtMap = {
106  'darwin' : '.dylib',
107  'win32' : '.dll'
108  }
109  return pExtMap.get(thePlatform, '.so')
110 
112  print "Welcome to ROOTaaS %s" %ROOT.gROOT.GetVersion()
113 
114 @contextmanager
115 def _setIgnoreLevel(level):
116  originalLevel = ROOT.gErrorIgnoreLevel
117  ROOT.gErrorIgnoreLevel = level
118  yield
119  ROOT.gErrorIgnoreLevel = originalLevel
120 
121 def commentRemover( text ):
122  '''
123  >>> s="// hello"
124  >>> commentRemover(s)
125  ''
126  >>> s="int /** Test **/ main() {return 0;}"
127  >>> commentRemover(s)
128  'int main() {return 0;}'
129  '''
130  def blotOutNonNewlines( strIn ) : # Return a string containing only the newline chars contained in strIn
131  return "" + ("\n" * strIn.count('\n'))
132 
133  def replacer( match ) :
134  s = match.group(0)
135  if s.startswith('/'): # Matched string is //...EOL or /*...*/ ==> Blot out all non-newline chars
136  return blotOutNonNewlines(s)
137  else: # Matched string is '...' or "..." ==> Keep unchanged
138  return s
139 
140  pattern = re.compile(\
141  r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
142  re.DOTALL | re.MULTILINE)
143 
144  return re.sub(pattern, replacer, text)
145 
146 
147 # Here functions are defined to process C++ code
149  #code = commentRemover(code)
150  ROOT.gInterpreter.ProcessLine(code)
151 
153  #code = commentRemover(code)
154  ROOT.gInterpreter.Declare(code)
155 
156 def processCppCode(code):
157  processCppCodeImpl(code)
158 
159 def declareCppCode(code):
160  declareCppCodeImpl(code)
161 
162 def _checkOutput(command,errMsg=None):
163  out = ""
164  try:
165  out = check_output(command.split())
166  except:
167  if errMsg:
168  sys.stderr.write("%s (command was %s)\n" %(errMsg,command))
169  return out
170 
171 def _invokeAclicMac(fileName):
172  '''FIXME!
173  This function is a workaround. On osx, it is impossible to link against
174  libzmq.so, among the others. The error is known and is
175  "ld: can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"
176  We cannot at the moment force Aclic to change the linker command in order
177  to exclude these libraries, so we launch a second root session to compile
178  the library, which we then load.
179  '''
180  command = 'root -l -q -b -e gSystem->CompileMacro(\"%s\",\"k\")*0'%fileName
181  out = _checkOutput(command, "Error ivoking ACLiC")
182  libNameBase = fileName.replace(".C","_C")
183  ROOT.gSystem.Load(libNameBase)
184 
185 def _codeToFilename(code):
186  '''Convert code to a unique file name
187 
188  >>> _codeToFilename("int f(i){return i*i;}")
189  'dbf7e731.C'
190  '''
191  fileNameBase = sha1(code).hexdigest()[0:8]
192  return fileNameBase + ".C"
193 
195  '''Dump code to file whose name is unique
196 
197  >>> _codeToFilename("int f(i){return i*i;}")
198  'dbf7e731.C'
199  '''
200  fileName = _codeToFilename(code)
201  with open (fileName,'w') as ofile:
202  ofile.write(code)
203  return fileName
204 
205 def invokeAclic(cell):
206  fileName = _dumpToUniqueFile(cell)
207  if _getPlatform() == 'darwin':
208  _invokeAclicMac(fileName)
209  else:
210  processCppCode(".L %s+" %fileName)
211 
212 class StreamCapture(object):
213  def __init__(self, stream, ip=get_ipython()):
214  nbStreamsPyStreamsMap={sys.stderr:sys.__stderr__,sys.stdout:sys.__stdout__}
215  self.shell = ip
216  self.nbStream = stream
217  self.pyStream = nbStreamsPyStreamsMap[stream]
218  self.pipe_out, self.pipe_in = pty.openpty()
219  os.dup2(self.pipe_in, self.pyStream.fileno())
220  # Platform independent flush
221  # With ctypes, the name of the libc library is not known a priori
222  # We use jitted function
223  flushFunctionName='_ROOTaaS_Flush'
224  if (not hasattr(ROOT,flushFunctionName)):
225  declareCppCode("void %s(){fflush(nullptr);};" %flushFunctionName)
226  self.flush = getattr(ROOT,flushFunctionName)
227 
228  def more_data(self):
229  r, _, _ = select.select([self.pipe_out], [], [], 0)
230  return bool(r)
231 
232  def post_execute(self):
233  out = ''
234  if self.pipe_out:
235  while self.more_data():
236  out += os.read(self.pipe_out, 8192)
237 
238  self.flush()
239  self.nbStream.write(out) # important to print the value printing output
240  return 0
241 
242  def register(self):
243  self.shell.events.register('post_execute', self.post_execute)
244 
245 class CaptureDrawnCanvases(object):
246  '''
247  Capture the canvas which is drawn to display it.
248  '''
249  def __init__(self, ip=get_ipython()):
250  self.shell = ip
251 
252  def _pre_execute(self):
253  pass
254 
255  def _post_execute(self):
256  for can in ROOT.gROOT.GetListOfCanvases():
257  if can.IsDrawn():
258  can.Draw()
259  can.ResetDrawn()
260 
261  def register(self):
262  self.shell.events.register('pre_execute', self._pre_execute)
263  self.shell.events.register('post_execute', self._post_execute)
264 
265 
266 captures = [StreamCapture(sys.stderr),
267  StreamCapture(sys.stdout),
269 
270 def toCpp():
271  '''
272  Change the mode of the notebook to CPP. It is preferred to use cell magic,
273  but this option is handy to set up servers and for debugging purposes.
274  '''
275  ip = get_ipython()
276  cpptransformer.load_ipython_extension(ip)
277  # Change highlight mode
278  IPython.display.display_javascript(_jsDefaultHighlight.format(mimeType = cppMIME), raw=True)
279  print "Notebook is in Cpp mode"
280 
281 class CanvasDrawer(object):
282  '''
283  Capture the canvas which is drawn and decide if it should be displayed using
284  jsROOT.
285  '''
286  jsUID = 0
287 
288  def __init__(self, thePad):
289  self.thePad = thePad
290 
292  """
293  Get the list of primitives in the pad, recursively descending into
294  histograms and graphs looking for fitted functions.
295  """
296  primitives = self.thePad.GetListOfPrimitives()
297  primitivesNames = map(lambda p: p.ClassName(), primitives)
298  #primitivesWithFunctions = filter(lambda primitive: hasattr(primitive,"GetListOfFunctions"), primitives)
299  #for primitiveWithFunctions in primitivesWithFunctions:
300  # for function in primitiveWithFunctions.GetListOfFunctions():
301  # primitivesNames.append(function.GetName())
302  return sorted(primitivesNames)
303 
304  def _getUID(self):
305  '''
306  Every DIV containing a JavaScript snippet must be unique in the
307  notebook. This methods provides a unique identifier.
308  '''
309  CanvasDrawer.jsUID += 1
310  return CanvasDrawer.jsUID
311 
312  def _canJsDisplay(self):
313  # to be optimised
314  if not _enableJSVis: return False
315  primitivesTypesNames = self._getListOfPrimitivesNamesAndTypes()
316  for unsupportedPattern in _jsNotDrawableClassesPatterns:
317  for primitiveTypeName in primitivesTypesNames:
318  if fnmatch.fnmatch(primitiveTypeName,unsupportedPattern):
319  print >> sys.stderr, "The canvas contains an object of a type jsROOT cannot currently handle (%s). Falling back to a static png." %primitiveTypeName
320  return False
321  return True
322 
323  def _jsDisplay(self):
324  # Workaround to have ConvertToJSON work
325  pad = ROOT.gROOT.GetListOfCanvases().FindObject(ROOT.gPad.GetName())
326  json = ROOT.TBufferJSON.ConvertToJSON(pad, 3)
327  #print "JSON:",json
328 
329  # Here we could optimise the string manipulation
330  divId = 'root_plot_' + str(self._getUID())
331  thisJsCode = _jsCode.format(jsCanvasWidth = _jsCanvasWidth,
332  jsCanvasHeight = _jsCanvasHeight,
333  jsROOTSourceDir = _jsROOTSourceDir,
334  jsonContent=json.Data(),
335  jsDrawOptions="",
336  jsDivId = divId)
337 
338  # display is the key point of this hook
339  IPython.display.display(HTML(thisJsCode))
340  return 0
341 
342  def _pngDisplay(self):
343  ofile = tempfile.NamedTemporaryFile(suffix=".png")
344  with _setIgnoreLevel(ROOT.kError):
345  self.thePad.SaveAs(ofile.name)
346  img = IPython.display.Image(filename=ofile.name, format='png', embed=True)
347  IPython.display.display(img)
348  return 0
349 
350  def _display(self):
351  if _enableJSVisDebug:
352  self._pngDisplay()
353  self._jsDisplay()
354  else:
355  if self._canJsDisplay():
356  self._jsDisplay()
357  else:
358  self._pngDisplay()
359 
360 
361  def Draw(self):
362  self._display()
363  return 0
364 
365 def _PyDraw(thePad):
366  """
367  Invoke the draw function and intercept the graphics
368  """
369  drawer = CanvasDrawer(thePad)
370  drawer.Draw()
371 
372 
373 def setStyle():
374  style=ROOT.gStyle
375  style.SetFuncWidth(3)
376  style.SetHistLineWidth(3)
377  style.SetMarkerStyle(8)
378  style.SetMarkerSize(.5)
379  style.SetMarkerColor(ROOT.kBlue)
380  style.SetPalette(57)
381 
383  extNames = ["ROOTaaS.iPyROOT." + name for name in ["cppmagic"]]
384  ip = get_ipython()
385  extMgr = ExtensionManager(ip)
386  for extName in extNames:
387  extMgr.load_extension(extName)
388  cppcompleter.load_ipython_extension(ip)
389 
390  for capture in captures: capture.register()
391 
393  ROOT.toCpp = toCpp
394  ROOT.enableJSVis = enableJSVis
395  ROOT.disableJSVis = disableJSVis
396  ROOT.enableJSVisDebug = enableJSVisDebug
397  ROOT.disableJSVisDebug = disableJSVisDebug
398  ROOT.TCanvas.DrawCpp = ROOT.TCanvas.Draw
399  ROOT.TCanvas.Draw = _PyDraw
400 
402  ipDispJs = IPython.display.display_javascript
403  #Make sure clike JS lexer is loaded
404  ipDispJs("require(['codemirror/mode/clike/clike'], function(Clike) { console.log('ROOTaaS - C++ CodeMirror module loaded'); });", raw=True)
405  # Define highlight mode for %%cpp magic
406  ipDispJs(_jsMagicHighlight.format(cppMIME = cppMIME), raw=True)
407 
409  setStyle()
413  welcomeMsg()
414 
def _getLibExtension(thePlatform)
Definition: utils.py:96
def invokeAclic(cell)
Definition: utils.py:205
double write(int n, const std::string &file_name, const std::string &vector_type, int compress=0)
writing
def __init__(self, thePad)
Definition: utils.py:288
def enhanceROOTModule()
Definition: utils.py:392
def declareCppCodeImpl(code)
Definition: utils.py:152
def loadExtensionsAndCapturers()
Definition: utils.py:382
def disableJSVisDebug()
Definition: utils.py:87
def __init__(self, ip=get_ipython())
Definition: utils.py:249
def _getListOfPrimitivesNamesAndTypes(self)
Definition: utils.py:291
def _dumpToUniqueFile(code)
Definition: utils.py:194
def _invokeAclicMac(fileName)
Definition: utils.py:171
def disableJSVis()
Definition: utils.py:77
def processCppCodeImpl(code)
Definition: utils.py:148
def enableCppHighlighting()
Definition: utils.py:401
def _setIgnoreLevel(level)
Definition: utils.py:115
def processCppCode(code)
Definition: utils.py:156
def _PyDraw(thePad)
Definition: utils.py:365
def declareCppCode(code)
Definition: utils.py:159
def enableJSVisDebug()
Definition: utils.py:81
def commentRemover(text)
Definition: utils.py:121
def _codeToFilename(code)
Definition: utils.py:185
def _checkOutput(command, errMsg=None)
Definition: utils.py:162
def __init__(self, stream, ip=get_ipython())
Definition: utils.py:213
def _getPlatform()
Definition: utils.py:93