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