CMS 3D CMS Logo

/data/git/CMSSW_5_3_11_patch5/src/PhysicsTools/PythonAnalysis/python/rootplot/utilities.py

Go to the documentation of this file.
00001 """
00002 Utilities for rootplot including histogram classes.
00003 """
00004 
00005 __license__ = '''\
00006 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
00007 
00008 Permission is hereby granted, free of charge, to any person obtaining a copy
00009 of this software and associated documentation files (the "Software"), to deal
00010 in the Software without restriction, including without limitation the rights
00011 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00012 copies of the Software, and to permit persons to whom the Software is
00013 furnished to do so, subject to the following conditions:
00014 
00015 The above copyright notice and this permission notice shall be included in
00016 all copies or substantial portions of the Software.
00017 
00018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00021 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00023 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00024 THE SOFTWARE.
00025 '''
00026 
00027 ################ Import python libraries
00028 
00029 import math
00030 import ROOT
00031 import re
00032 import copy
00033 import array
00034 import os.path
00035 import sys
00036 import fnmatch
00037 from random import gauss
00038 
00039 ################ Define classes
00040 
00041 class Hist2D(object):
00042     """A container to hold the parameters from a 2D ROOT histogram."""
00043     def __init__(self, hist, label="__nolabel__", title=None,
00044                  xlabel=None, ylabel=None):
00045         try:
00046             if not hist.InheritsFrom("TH2"):
00047                 raise TypeError("%s does not inherit from TH2" % hist)
00048         except:
00049             raise TypeError("%s is not a ROOT object" % hist)
00050         self.rootclass = hist.ClassName()
00051         self.name = hist.GetName()
00052         self.nbinsx = nx = hist.GetNbinsX()
00053         self.nbinsy = ny = hist.GetNbinsY()
00054         self.binlabelsx = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
00055                                                for i in range(1, nx + 1)])
00056         if self.binlabelsx:
00057             self.nbinsx = nx = self.binlabelsx.index('')
00058             self.binlabelsx = self.binlabelsx[:ny]
00059         self.binlabelsy = process_bin_labels([hist.GetYaxis().GetBinLabel(i)
00060                                                for i in range(1, ny + 1)])
00061         if self.binlabelsy:
00062             self.nbinsy = ny = self.binlabelsy.index('')
00063             self.binlabelsy = self.binlabelsy[:ny]
00064         self.entries = hist.GetEntries()
00065         self.content = [[hist.GetBinContent(i, j) for i in range(1, nx + 1)]
00066                         for j in range(1, ny + 1)]
00067         self.xedges = [hist.GetXaxis().GetBinLowEdge(i)
00068                              for i in range(1, nx + 2)]
00069         self.yedges = [hist.GetYaxis().GetBinLowEdge(i)
00070                              for i in range(1, ny + 2)]
00071         self.x      = [(self.xedges[i+1] + self.xedges[i])/2
00072                              for i in range(nx)]
00073         self.y      = [(self.yedges[i+1] + self.yedges[i])/2
00074                              for i in range(ny)]
00075         self.title  = title or hist.GetTitle()
00076         self.xlabel = xlabel or hist.GetXaxis().GetTitle()
00077         self.ylabel = ylabel or hist.GetYaxis().GetTitle()
00078         self.label  = label
00079     def _flat_content(self):
00080         flatcontent = []
00081         for row in self.content:
00082             flatcontent += row
00083         return flatcontent
00084     def __getitem__(self, index):
00085         """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
00086         return self._flat_content()[index]
00087     def __len__(self):
00088         """Return the number of bins: x.__len__() <==> len(x)"""
00089         return len(self._flat_content())
00090     def __iter__(self):
00091         """Iterate through bins: x.__iter__() <==> iter(x)"""
00092         return iter(self._flat_content())
00093     def TH2F(self, name=""):
00094         """Return a ROOT.TH2F object with contents of this Hist2D."""
00095         th2f = ROOT.TH2F(name, "",
00096                          self.nbinsx, array.array('f', self.xedges),
00097                          self.nbinsy, array.array('f', self.yedges))
00098         th2f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
00099         for ix in range(self.nbinsx):
00100             for iy in range(self.nbinsy):
00101                 th2f.SetBinContent(ix + 1, iy + 1, self.content[iy][ix])
00102         return th2f
00103 
00104 class Hist(object):
00105     """A container to hold the parameters from a ROOT histogram."""
00106     def __init__(self, hist, label="__nolabel__",
00107                  name=None, title=None, xlabel=None, ylabel=None):
00108         try:
00109             hist.GetNbinsX()
00110             self.__init_TH1(hist)
00111         except AttributeError:
00112             try:
00113                 hist.GetN()
00114                 self.__init_TGraph(hist)
00115             except AttributeError:
00116                 raise TypeError("%s is not a 1D histogram or TGraph" % hist)
00117         self.rootclass = hist.ClassName()
00118         self.name = name or hist.GetName()
00119         self.title  = title or hist.GetTitle().split(';')[0]
00120         self.xlabel = xlabel or hist.GetXaxis().GetTitle()
00121         self.ylabel = ylabel or hist.GetYaxis().GetTitle()
00122         self.label  = label
00123     def __init_TH1(self, hist):
00124         self.nbins = n = hist.GetNbinsX()
00125         self.binlabels = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
00126                                              for i in range(1, n + 1)])
00127         if self.binlabels and '' in self.binlabels:
00128             # Get rid of extra non-labeled bins
00129             self.nbins = n = self.binlabels.index('')
00130             self.binlabels = self.binlabels[:n]
00131         self.entries = hist.GetEntries()
00132         self.xedges = [hist.GetBinLowEdge(i) for i in range(1, n + 2)]
00133         self.x      = [(self.xedges[i+1] + self.xedges[i])/2 for i in range(n)]
00134         self.xerr   = [(self.xedges[i+1] - self.xedges[i])/2 for i in range(n)]
00135         self.xerr   = [self.xerr[:], self.xerr[:]]
00136         self.width  = [(self.xedges[i+1] - self.xedges[i])   for i in range(n)]
00137         self.y      = [hist.GetBinContent(i) for i in range(1, n + 1)]
00138         self.yerr   = [hist.GetBinError(  i) for i in range(1, n + 1)]
00139         self.yerr   = [self.yerr[:], self.yerr[:]]
00140         self.underflow = hist.GetBinContent(0)
00141         self.overflow  = hist.GetBinContent(self.nbins + 1)
00142     def __init_TGraph(self, hist):
00143         self.nbins = n = hist.GetN()
00144         self.x, self.y = [], []
00145         x, y = ROOT.Double(0), ROOT.Double(0)
00146         for i in range(n):
00147             hist.GetPoint(i, x, y)
00148             self.x.append(copy.copy(x))
00149             self.y.append(copy.copy(y))
00150         lower = [max(0, hist.GetErrorXlow(i))  for i in xrange(n)]
00151         upper = [max(0, hist.GetErrorXhigh(i)) for i in xrange(n)]
00152         self.xerr = [lower[:], upper[:]]
00153         lower = [max(0, hist.GetErrorYlow(i))  for i in xrange(n)]
00154         upper = [max(0, hist.GetErrorYhigh(i)) for i in xrange(n)]
00155         self.yerr = [lower[:], upper[:]]
00156         self.xedges = [self.x[i] - self.xerr[0][i] for i in xrange(n)]
00157         self.xedges.append(self.x[n - 1] + self.xerr[1][n - 1])
00158         self.width = [self.xedges[i + 1] - self.xedges[i] for i in range(n)]
00159         self.underflow, self.overflow = 0, 0
00160         self.binlabels = None
00161         self.entries = n
00162     def __add__(self, b):
00163         """Return the sum of self and b: x.__add__(y) <==> x + y"""
00164         c = copy.copy(self)
00165         for i in range(len(self)):
00166             c.y[i] += b.y[i]
00167             c.yerr[0][i] += b.yerr[0][i]
00168             c.yerr[1][i] += b.yerr[1][i]
00169         c.overflow += b.overflow
00170         c.underflow += b.underflow
00171         return c
00172     def __sub__(self, b):
00173         """Return the difference of self and b: x.__sub__(y) <==> x - y"""
00174         c = copy.copy(self)
00175         for i in range(len(self)):
00176             c.y[i] -= b.y[i]
00177             c.yerr[0][i] -= b.yerr[0][i]
00178             c.yerr[1][i] -= b.yerr[1][i]
00179         c.overflow -= b.overflow
00180         c.underflow -= b.underflow
00181         return c
00182     def __div__(self, denominator):
00183         return self.divide(denominator)
00184     def __getitem__(self, index):
00185         """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
00186         return self.y[index]
00187     def __setitem__(self, index, value):
00188         """Set contents of indexth bin: x.__setitem__(i, y) <==> x[i]=y"""
00189         self.y[index] = value
00190     def __len__(self):
00191         """Return the number of bins: x.__len__() <==> len(x)"""
00192         return self.nbins
00193     def __iter__(self):
00194         """Iterate through bins: x.__iter__() <==> iter(x)"""
00195         return iter(self.y)
00196     def min(self, threshold=None):
00197         """Return the y-value of the bottom tip of the lowest errorbar."""
00198         vals = [(yval - yerr) for yval, yerr in zip(self.y, self.yerr[0])
00199                 if (yval - yerr) > threshold]
00200         if vals:
00201             return min(vals)
00202         else:
00203             return threshold
00204     def av_xerr(self):
00205         """Return average between the upper and lower xerr."""
00206         return [(self.xerr[0][i] + self.xerr[1][i]) / 2
00207                 for i in range(self.nbins)]
00208     def av_yerr(self):
00209         """Return average between the upper and lower yerr."""
00210         return [(self.yerr[0][i] + self.yerr[1][i]) / 2
00211                 for i in range(self.nbins)]
00212     def scale(self, factor):
00213         """
00214         Scale contents, errors, and over/underflow by the given scale factor.
00215         """
00216         self.y = [x * factor for x in self.y]
00217         self.yerr[0] = [x * factor for x in self.yerr[0]]
00218         self.yerr[1] = [x * factor for x in self.yerr[1]]
00219         self.overflow *= factor
00220         self.underflow *= factor
00221     def delete_bin(self, index):
00222         """
00223         Delete a the contents of a bin, sliding all the other data one bin to
00224         the left.  This can be useful for histograms with labeled bins.
00225         """
00226         self.nbins -= 1
00227         self.xedges.pop()
00228         self.x.pop()
00229         self.width.pop()
00230         self.y.pop(index)
00231         self.xerr[0].pop(index)
00232         self.xerr[1].pop(index)
00233         self.yerr[0].pop(index)
00234         self.yerr[1].pop(index)
00235         if self.binlabels:
00236             self.binlabels.pop(index)
00237     def TH1F(self, name=None):
00238         """Return a ROOT.TH1F object with contents of this Hist"""
00239         th1f = ROOT.TH1F(name or self.name, "", self.nbins,
00240                          array.array('f', self.xedges))
00241         th1f.Sumw2()
00242         th1f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
00243         for i in range(self.nbins):
00244             th1f.SetBinContent(i + 1, self.y[i])
00245             try:
00246                 th1f.SetBinError(i + 1, (self.yerr[0][i] + self.yerr[1][i]) / 2)
00247             except TypeError:
00248                 th1f.SetBinError(i + 1, self.yerr[i])
00249             if self.binlabels:
00250                 th1f.GetXaxis().SetBinLabel(i + 1, self.binlabels[i])
00251         th1f.SetBinContent(0, self.underflow)
00252         th1f.SetBinContent(self.nbins + 2, self.overflow)
00253         th1f.SetEntries(self.entries)
00254         return th1f
00255     def TGraph(self, name=None):
00256         """Return a ROOT.TGraphAsymmErrors object with contents of this Hist"""
00257         x = array.array('f', self.x)
00258         y = array.array('f', self.y)
00259         xl = array.array('f', self.xerr[0])
00260         xh = array.array('f', self.xerr[1])
00261         yl = array.array('f', self.yerr[0])
00262         yh = array.array('f', self.yerr[1])
00263         tgraph = ROOT.TGraphAsymmErrors(self.nbins, x, y, xl, xh, yl, yh)
00264         tgraph.SetName(name or self.name)
00265         tgraph.SetTitle('%s;%s;%s' % (self.title, self.xlabel, self.ylabel))
00266         return tgraph
00267     def divide(self, denominator):
00268         """
00269         Return the simple quotient with errors added in quadrature.
00270 
00271         This function is called by the division operator:
00272             hist3 = hist1.divide_wilson(hist2) <--> hist3 = hist1 / hist2
00273         """
00274         if len(self) != len(denominator):
00275             raise TypeError("Cannot divide %s with %i bins by "
00276                             "%s with %i bins." % 
00277                             (denominator.name, len(denominator), 
00278                              self.name, len(self)))
00279         quotient = copy.deepcopy(self)
00280         num_yerr = self.av_yerr()
00281         den_yerr = denominator.av_yerr()
00282         quotient.yerr = [0. for i in range(self.nbins)]
00283         for i in range(self.nbins):
00284             if denominator[i] == 0 or self[i] == 0:
00285                 quotient.y[i] = 0.
00286             else:
00287                 quotient.y[i] = self[i] / denominator[i]
00288                 quotient.yerr[i] = quotient[i]
00289                 quotient.yerr[i] *= math.sqrt((num_yerr[i] / self[i]) ** 2 +
00290                                        (den_yerr[i] / denominator[i]) ** 2)
00291             if quotient.yerr[i] > quotient[i]:
00292                 quotient.yerr[i] = quotient[i]
00293         quotient.yerr = [quotient.yerr, quotient.yerr]
00294         return quotient
00295     def divide_wilson(self, denominator):
00296         """Return an efficiency plot with Wilson score interval errors."""
00297         if len(self) != len(denominator):
00298             raise TypeError("Cannot divide %s with %i bins by "
00299                             "%s with %i bins." % 
00300                             (denominator.name, len(denominator), 
00301                              self.name, len(self)))
00302         eff, upper_err, lower_err = wilson_interval(self.y, denominator.y)
00303         quotient = copy.deepcopy(self)
00304         quotient.y = eff
00305         quotient.yerr = [lower_err, upper_err]
00306         return quotient
00307 
00308 class HistStack(object):
00309     """
00310     A container to hold Hist objects for plotting together.
00311 
00312     When plotting, the title and the x and y labels of the last Hist added
00313     will be used unless specified otherwise in the constructor.
00314     """
00315     def __init__(self, hists=None, title=None, xlabel=None, ylabel=None):
00316         self.hists  = []
00317         self.kwargs = []
00318         self.title  = title
00319         self.xlabel = xlabel
00320         self.ylabel = ylabel
00321         if hists:
00322             for hist in hists:
00323                 self.add(hist)
00324     def __getitem__(self, index):
00325         """Return indexth hist: x.__getitem__(y) <==> x[y]"""
00326         return self.hists[index]
00327     def __setitem__(self, index, value):
00328         """Replace indexth hist with value: x.__setitem__(i, y) <==> x[i]=y"""
00329         self.hists[index] = value
00330     def __len__(self):
00331         """Return the number of hists in the stack: x.__len__() <==> len(x)"""
00332         return len(self.hists)
00333     def __iter__(self):
00334         """Iterate through hists in the stack: x.__iter__() <==> iter(x)"""
00335         return iter(self.hists)
00336     def max(self):
00337         """Return the value of the highest bin of all hists in the stack."""
00338         maxes = [max(x) for x in self.hists]
00339         try:
00340             return max(maxes)
00341         except ValueError:
00342             return 0
00343     def stackmax(self):
00344         """Return the value of the highest bin in the addition of all hists."""
00345         try:
00346             return max([sum([h[i] for h in self.hists])
00347                        for i in range(self.hists[0].nbins)])
00348         except:
00349             print [h.nbins for h in self.hists]
00350     def scale(self, factor):
00351         """Scale all Hists by factor."""
00352         for hist in self.hists:
00353             hist.scale(factor)
00354     def min(self, threshold=None):
00355         """
00356         Return the value of the lowest bin of all hists in the stack.
00357 
00358         If threshold is specified, only values above the threshold will be
00359         considered.
00360         """
00361         mins = [x.min(threshold) for x in self.hists]
00362         return min(mins)
00363     def add(self, hist, **kwargs):
00364         """
00365         Add a Hist object to this stack.
00366 
00367         Any additional keyword arguments will be added to just this Hist
00368         when the stack is plotted.
00369         """
00370         if "label" in kwargs:
00371             hist.label = kwargs['label']
00372             del kwargs['label']
00373         if len(self) > 0:
00374             if hist.xedges != self.hists[0].xedges:
00375                 raise ValueError("Cannot add %s to stack; all Hists must "
00376                                  "have the same binning." % hist.name)
00377         self.hists.append(hist)
00378         self.kwargs.append(kwargs)
00379 
00380 
00381 ################ Define functions and classes for navigating within ROOT
00382 
00383 class RootFile(object):
00384     """A wrapper for TFiles, allowing easier access to methods."""
00385     def __init__(self, filename, name=None):
00386         self.filename = filename
00387         self.name = name or filename[:-5]
00388         self.file = ROOT.TFile(filename, 'read')
00389         if self.file.IsZombie():
00390             raise ValueError("Error opening %s" % filename)
00391     def cd(self, directory=''):
00392         """Make directory the current directory."""
00393         self.file.cd(directory)
00394     def get(self, object_name, path=None, type1D=Hist, type2D=Hist2D):
00395         """Return a Hist object from the given path within this file."""
00396         if not path:
00397             path = os.path.dirname(object_name)
00398             object_name = os.path.basename(object_name)
00399         try:
00400             roothist = self.file.GetDirectory(path).Get(object_name)
00401         except ReferenceError, e:
00402             raise ReferenceError(e)
00403         try:
00404             return type2D(roothist)
00405         except TypeError:
00406             return type1D(roothist)
00407 
00408 def ls(directory=None):
00409     """Return a python list of ROOT object names from the given directory."""
00410     if directory == None:
00411         keys = ROOT.gDirectory.GetListOfKeys()
00412     else:
00413         keys = ROOT.gDirectory.GetDirectory(directory).GetListOfKeys()
00414     key = keys[0]
00415     names = []
00416     while key:
00417         obj = key.ReadObj()
00418         key = keys.After(key)
00419         names.append(obj.GetName())
00420     return names
00421 
00422 def pwd():
00423     """Return ROOT's present working directory."""
00424     return ROOT.gDirectory.GetPath()
00425 
00426 def get(object_name):
00427     """Return a Hist object with the given name."""
00428     return Hist(ROOT.gDirectory.Get(object_name))
00429 
00430 
00431 ################ Define additional helping functions
00432 
00433 def loadROOT(batch=True):
00434     ## We need to temporarily change sys.argv so that ROOT doesn't intercept 
00435     ## options from the command-line
00436     saved_argv = sys.argv[:]
00437     argstring = ' '.join(sys.argv)
00438     sys.argv = [sys.argv[0]]
00439     try:
00440         import ROOT
00441     except ImportError:
00442         print """\
00443 The program was unable to access PyROOT.  Usually, this just requires switching
00444 to the same major version of python that used when compiling ROOT.  To
00445 determine which version that is, try the following command:
00446     root -config 2>&1 | tr ' ' '\\n' | egrep 'python|PYTHON'
00447 If this is different from the python version you are currently using, try
00448 changing your PATH to point to the new one."""
00449         sys.exit(1)
00450     ## Enter batch mode, unless outputting to C macros
00451     ## There is a bug in pyROOT that fails to export colors in batch mode
00452     if batch:
00453         ROOT.gROOT.SetBatch()
00454     ROOT.gErrorIgnoreLevel = ROOT.kWarning
00455     ## PyROOT picks up ~/.rootlogon if it exists, but not ./rootlogon.C 
00456     if os.path.exists('rootlogon.C'):
00457         ROOT.gROOT.Macro('rootlogon.C')
00458     sys.argv = saved_argv[:]
00459     return ROOT
00460 
00461 def replace(string, replacements):
00462     """
00463     Modify a string based on a list of patterns and substitutions.
00464 
00465     replacements should be a list of two-entry tuples, the first entry giving
00466     a string to search for and the second entry giving the string with which
00467     to replace it.  If replacements includes a pattern entry containing
00468     'use_regexp', then all patterns will be treated as regular expressions
00469     using re.sub.
00470     """
00471     if not replacements:
00472         return string
00473     if 'use_regexp' in [x for x,y in replacements]:
00474         for pattern, repl in [x for x in replacements
00475                               if x[0] != 'use_regexp']:
00476             string = re.sub(pattern, repl, string)
00477     else:
00478         for pattern, repl in replacements:
00479             string = string.replace(pattern, repl)
00480     if re.match(_all_whitespace_string, string):
00481         return ""
00482     return string
00483 
00484 def process_bin_labels(binlabels):
00485     has_labels = False
00486     for binlabel in binlabels:
00487         if binlabel:
00488             has_labels = True
00489     if has_labels:
00490         return binlabels
00491     else:
00492         return None
00493 
00494 def wilson_interval(numerator_array, denominator_array):
00495     eff, upper_err, lower_err = [], [], []
00496     for n, d in zip(numerator_array, denominator_array):
00497         try:
00498             p = float(n) / d
00499             s = math.sqrt(p * (1 - p) / d + 1 / (4 * d * d))
00500             t = p + 1 / (2 * d)
00501             eff.append(p)
00502             upper_err.append(-p + 1/(1 + 1/d) * (t + s))
00503             lower_err.append(+p - 1/(1 + 1/d) * (t - s))
00504         except ZeroDivisionError:
00505             eff.append(0)
00506             upper_err.append(0)
00507             lower_err.append(0)
00508     return eff, upper_err, lower_err
00509 
00510 def find_num_processors():
00511     import os
00512     try:
00513         num_processors = os.sysconf('SC_NPROCESSORS_ONLN')
00514     except:
00515         try:
00516             num_processors = os.environ['NUMBER_OF_PROCESSORS']
00517         except:
00518             num_processors = 1
00519     return num_processors
00520 
00521 def testfile():
00522     outfile = ROOT.TFile("test.root", "recreate")
00523     for i in range(4):
00524         d = outfile.mkdir("dir%i" % (i + 1))
00525         d.cd()
00526         for j in range(4):
00527             hist = ROOT.TH1F("hist%i" % (j + 1), "A Histogram", 10, 0, 10)
00528             hist.Fill(j)
00529             hist.Write()
00530     outfile.Write()
00531     return outfile
00532 
00533 #### Functions for globbing within root files
00534 
00535 glob_magic_check = re.compile('[*?[]')
00536 
00537 def has_glob_magic(s):
00538     return glob_magic_check.search(s) is not None
00539 
00540 # These 2 helper functions non-recursively glob inside a literal directory.
00541 # They return a list of basenames. `_rootglob1` accepts a pattern while 
00542 # `_rootglob0` takes a literal basename (so it only has to check for its 
00543 # existence).
00544 
00545 def _rootglob1(tdirectory, dirname, pattern):
00546     if not tdirectory.GetDirectory(dirname):
00547         return []
00548     names = [key.GetName() for key in 
00549              tdirectory.GetDirectory(dirname).GetListOfKeys()]
00550     return fnmatch.filter(names, pattern)
00551 
00552 def _rootglob0(tdirectory, dirname, basename):
00553     if tdirectory.Get(os.path.join(dirname, basename)):
00554         return [basename]
00555     return []
00556 
00557 def rootglob(tdirectory, pathname):
00558     """Return a list of paths matching a pathname pattern.
00559 
00560     The pattern may contain simple shell-style wildcards a la fnmatch.
00561 
00562     >>> import rootplot.utilities
00563     >>> f = rootplot.utilities.testfile()
00564     >>> rootglob(f, '*')
00565     ['dir1', 'dir2', 'dir3', 'dir4']
00566     >>> rootglob(f, 'dir1/*')
00567     ['dir1/hist1', 'dir1/hist2', 'dir1/hist3', 'dir1/hist4']
00568     >>> rootglob(f, '*/hist1')
00569     ['dir1/hist1', 'dir2/hist1', 'dir3/hist1', 'dir4/hist1']
00570     >>> rootglob(f, 'dir1/hist[1-2]')
00571     ['dir1/hist1', 'dir1/hist2']
00572     """
00573     return list(irootglob(tdirectory, pathname))
00574 
00575 def irootglob(tdirectory, pathname):
00576     """Return an iterator which yields the paths matching a pathname pattern.
00577 
00578     The pattern may contain simple shell-style wildcards a la fnmatch.
00579 
00580     """
00581     if not has_glob_magic(pathname):
00582         if tdirectory.Get(pathname):
00583             yield pathname
00584         return
00585     dirname, basename = os.path.split(pathname)
00586     if has_glob_magic(dirname):
00587         dirs = irootglob(tdirectory, dirname)
00588     else:
00589         dirs = [dirname]
00590     if has_glob_magic(basename):
00591         glob_in_dir = _rootglob1
00592     else:
00593         glob_in_dir = _rootglob0
00594     for dirname in dirs:
00595         for name in glob_in_dir(tdirectory, dirname, basename):
00596             yield os.path.join(dirname, name)
00597 
00598 if __name__ == '__main__':
00599     import doctest
00600     doctest.testmod()