Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:24:07

0001 """
0002 Utilities for rootplot including histogram classes.
0003 """
0004 from __future__ import print_function
0005 
0006 from builtins import range
0007 __license__ = '''\
0008 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
0009 
0010 Permission is hereby granted, free of charge, to any person obtaining a copy
0011 of this software and associated documentation files (the "Software"), to deal
0012 in the Software without restriction, including without limitation the rights
0013 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0014 copies of the Software, and to permit persons to whom the Software is
0015 furnished to do so, subject to the following conditions:
0016 
0017 The above copyright notice and this permission notice shall be included in
0018 all copies or substantial portions of the Software.
0019 
0020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0026 THE SOFTWARE.
0027 '''
0028 
0029 ################ Import python libraries
0030 
0031 import math
0032 import ROOT
0033 import re
0034 import copy
0035 import array
0036 import os.path
0037 import sys
0038 import fnmatch
0039 from random import gauss
0040 
0041 ################ Define classes
0042 
0043 class Hist2D(object):
0044     """A container to hold the parameters from a 2D ROOT histogram."""
0045     def __init__(self, hist, label="__nolabel__", title=None,
0046                  xlabel=None, ylabel=None):
0047         try:
0048             if not hist.InheritsFrom("TH2"):
0049                 raise TypeError("%s does not inherit from TH2" % hist)
0050         except:
0051             raise TypeError("%s is not a ROOT object" % hist)
0052         self.rootclass = hist.ClassName()
0053         self.name = hist.GetName()
0054         self.nbinsx = nx = hist.GetNbinsX()
0055         self.nbinsy = ny = hist.GetNbinsY()
0056         self.binlabelsx = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
0057                                                for i in range(1, nx + 1)])
0058         if self.binlabelsx:
0059             self.nbinsx = nx = self.binlabelsx.index('')
0060             self.binlabelsx = self.binlabelsx[:ny]
0061         self.binlabelsy = process_bin_labels([hist.GetYaxis().GetBinLabel(i)
0062                                                for i in range(1, ny + 1)])
0063         if self.binlabelsy:
0064             self.nbinsy = ny = self.binlabelsy.index('')
0065             self.binlabelsy = self.binlabelsy[:ny]
0066         self.entries = hist.GetEntries()
0067         self.content = [[hist.GetBinContent(i, j) for i in range(1, nx + 1)]
0068                         for j in range(1, ny + 1)]
0069         self.xedges = [hist.GetXaxis().GetBinLowEdge(i)
0070                              for i in range(1, nx + 2)]
0071         self.yedges = [hist.GetYaxis().GetBinLowEdge(i)
0072                              for i in range(1, ny + 2)]
0073         self.x      = [(self.xedges[i+1] + self.xedges[i])/2
0074                              for i in range(nx)]
0075         self.y      = [(self.yedges[i+1] + self.yedges[i])/2
0076                              for i in range(ny)]
0077         self.title  = title or hist.GetTitle()
0078         self.xlabel = xlabel or hist.GetXaxis().GetTitle()
0079         self.ylabel = ylabel or hist.GetYaxis().GetTitle()
0080         self.label  = label
0081     def _flat_content(self):
0082         flatcontent = []
0083         for row in self.content:
0084             flatcontent += row
0085         return flatcontent
0086     def __getitem__(self, index):
0087         """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
0088         return self._flat_content()[index]
0089     def __len__(self):
0090         """Return the number of bins: x.__len__() <==> len(x)"""
0091         return len(self._flat_content())
0092     def __iter__(self):
0093         """Iterate through bins: x.__iter__() <==> iter(x)"""
0094         return iter(self._flat_content())
0095     def TH2F(self, name=""):
0096         """Return a ROOT.TH2F object with contents of this Hist2D."""
0097         th2f = ROOT.TH2F(name, "",
0098                          self.nbinsx, array.array('f', self.xedges),
0099                          self.nbinsy, array.array('f', self.yedges))
0100         th2f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
0101         for ix in range(self.nbinsx):
0102             for iy in range(self.nbinsy):
0103                 th2f.SetBinContent(ix + 1, iy + 1, self.content[iy][ix])
0104         return th2f
0105 
0106 class Hist(object):
0107     """A container to hold the parameters from a ROOT histogram."""
0108     def __init__(self, hist, label="__nolabel__",
0109                  name=None, title=None, xlabel=None, ylabel=None):
0110         try:
0111             hist.GetNbinsX()
0112             self.__init_TH1(hist)
0113         except AttributeError:
0114             try:
0115                 hist.GetN()
0116                 self.__init_TGraph(hist)
0117             except AttributeError:
0118                 raise TypeError("%s is not a 1D histogram or TGraph" % hist)
0119         self.rootclass = hist.ClassName()
0120         self.name = name or hist.GetName()
0121         self.title  = title or hist.GetTitle().split(';')[0]
0122         self.xlabel = xlabel or hist.GetXaxis().GetTitle()
0123         self.ylabel = ylabel or hist.GetYaxis().GetTitle()
0124         self.label  = label
0125     def __init_TH1(self, hist):
0126         self.nbins = n = hist.GetNbinsX()
0127         self.binlabels = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
0128                                              for i in range(1, n + 1)])
0129         if self.binlabels and '' in self.binlabels:
0130             # Get rid of extra non-labeled bins
0131             self.nbins = n = self.binlabels.index('')
0132             self.binlabels = self.binlabels[:n]
0133         self.entries = hist.GetEntries()
0134         self.xedges = [hist.GetBinLowEdge(i) for i in range(1, n + 2)]
0135         self.x      = [(self.xedges[i+1] + self.xedges[i])/2 for i in range(n)]
0136         self.xerr   = [(self.xedges[i+1] - self.xedges[i])/2 for i in range(n)]
0137         self.xerr   = [self.xerr[:], self.xerr[:]]
0138         self.width  = [(self.xedges[i+1] - self.xedges[i])   for i in range(n)]
0139         self.y      = [hist.GetBinContent(i) for i in range(1, n + 1)]
0140         self.yerr   = [hist.GetBinError(  i) for i in range(1, n + 1)]
0141         self.yerr   = [self.yerr[:], self.yerr[:]]
0142         self.underflow = hist.GetBinContent(0)
0143         self.overflow  = hist.GetBinContent(self.nbins + 1)
0144     def __init_TGraph(self, hist):
0145         self.nbins = n = hist.GetN()
0146         self.x, self.y = [], []
0147         x, y = ROOT.Double(0), ROOT.Double(0)
0148         for i in range(n):
0149             hist.GetPoint(i, x, y)
0150             self.x.append(copy.copy(x))
0151             self.y.append(copy.copy(y))
0152         lower = [max(0, hist.GetErrorXlow(i))  for i in range(n)]
0153         upper = [max(0, hist.GetErrorXhigh(i)) for i in range(n)]
0154         self.xerr = [lower[:], upper[:]]
0155         lower = [max(0, hist.GetErrorYlow(i))  for i in range(n)]
0156         upper = [max(0, hist.GetErrorYhigh(i)) for i in range(n)]
0157         self.yerr = [lower[:], upper[:]]
0158         self.xedges = [self.x[i] - self.xerr[0][i] for i in range(n)]
0159         self.xedges.append(self.x[n - 1] + self.xerr[1][n - 1])
0160         self.width = [self.xedges[i + 1] - self.xedges[i] for i in range(n)]
0161         self.underflow, self.overflow = 0, 0
0162         self.binlabels = None
0163         self.entries = n
0164     def __add__(self, b):
0165         """Return the sum of self and b: x.__add__(y) <==> x + y"""
0166         c = copy.copy(self)
0167         for i in range(len(self)):
0168             c.y[i] += b.y[i]
0169             c.yerr[0][i] += b.yerr[0][i]
0170             c.yerr[1][i] += b.yerr[1][i]
0171         c.overflow += b.overflow
0172         c.underflow += b.underflow
0173         return c
0174     def __sub__(self, b):
0175         """Return the difference of self and b: x.__sub__(y) <==> x - y"""
0176         c = copy.copy(self)
0177         for i in range(len(self)):
0178             c.y[i] -= b.y[i]
0179             c.yerr[0][i] -= b.yerr[0][i]
0180             c.yerr[1][i] -= b.yerr[1][i]
0181         c.overflow -= b.overflow
0182         c.underflow -= b.underflow
0183         return c
0184     def __div__(self, denominator):
0185         return self.divide(denominator)
0186     def __getitem__(self, index):
0187         """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
0188         return self.y[index]
0189     def __setitem__(self, index, value):
0190         """Set contents of indexth bin: x.__setitem__(i, y) <==> x[i]=y"""
0191         self.y[index] = value
0192     def __len__(self):
0193         """Return the number of bins: x.__len__() <==> len(x)"""
0194         return self.nbins
0195     def __iter__(self):
0196         """Iterate through bins: x.__iter__() <==> iter(x)"""
0197         return iter(self.y)
0198     def min(self, threshold=None):
0199         """Return the y-value of the bottom tip of the lowest errorbar."""
0200         vals = [(yval - yerr) for yval, yerr in zip(self.y, self.yerr[0])
0201                 if (yval - yerr) > threshold]
0202         if vals:
0203             return min(vals)
0204         else:
0205             return threshold
0206     def av_xerr(self):
0207         """Return average between the upper and lower xerr."""
0208         return [(self.xerr[0][i] + self.xerr[1][i]) / 2
0209                 for i in range(self.nbins)]
0210     def av_yerr(self):
0211         """Return average between the upper and lower yerr."""
0212         return [(self.yerr[0][i] + self.yerr[1][i]) / 2
0213                 for i in range(self.nbins)]
0214     def scale(self, factor):
0215         """
0216         Scale contents, errors, and over/underflow by the given scale factor.
0217         """
0218         self.y = [x * factor for x in self.y]
0219         self.yerr[0] = [x * factor for x in self.yerr[0]]
0220         self.yerr[1] = [x * factor for x in self.yerr[1]]
0221         self.overflow *= factor
0222         self.underflow *= factor
0223     def delete_bin(self, index):
0224         """
0225         Delete a the contents of a bin, sliding all the other data one bin to
0226         the left.  This can be useful for histograms with labeled bins.
0227         """
0228         self.nbins -= 1
0229         self.xedges.pop()
0230         self.x.pop()
0231         self.width.pop()
0232         self.y.pop(index)
0233         self.xerr[0].pop(index)
0234         self.xerr[1].pop(index)
0235         self.yerr[0].pop(index)
0236         self.yerr[1].pop(index)
0237         if self.binlabels:
0238             self.binlabels.pop(index)
0239     def TH1F(self, name=None):
0240         """Return a ROOT.TH1F object with contents of this Hist"""
0241         th1f = ROOT.TH1F(name or self.name, "", self.nbins,
0242                          array.array('f', self.xedges))
0243         th1f.Sumw2()
0244         th1f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
0245         for i in range(self.nbins):
0246             th1f.SetBinContent(i + 1, self.y[i])
0247             try:
0248                 th1f.SetBinError(i + 1, (self.yerr[0][i] + self.yerr[1][i]) / 2)
0249             except TypeError:
0250                 th1f.SetBinError(i + 1, self.yerr[i])
0251             if self.binlabels:
0252                 th1f.GetXaxis().SetBinLabel(i + 1, self.binlabels[i])
0253         th1f.SetBinContent(0, self.underflow)
0254         th1f.SetBinContent(self.nbins + 2, self.overflow)
0255         th1f.SetEntries(self.entries)
0256         return th1f
0257     def TGraph(self, name=None):
0258         """Return a ROOT.TGraphAsymmErrors object with contents of this Hist"""
0259         x = array.array('f', self.x)
0260         y = array.array('f', self.y)
0261         xl = array.array('f', self.xerr[0])
0262         xh = array.array('f', self.xerr[1])
0263         yl = array.array('f', self.yerr[0])
0264         yh = array.array('f', self.yerr[1])
0265         tgraph = ROOT.TGraphAsymmErrors(self.nbins, x, y, xl, xh, yl, yh)
0266         tgraph.SetName(name or self.name)
0267         tgraph.SetTitle('%s;%s;%s' % (self.title, self.xlabel, self.ylabel))
0268         return tgraph
0269     def divide(self, denominator):
0270         """
0271         Return the simple quotient with errors added in quadrature.
0272 
0273         This function is called by the division operator:
0274             hist3 = hist1.divide_wilson(hist2) <--> hist3 = hist1 / hist2
0275         """
0276         if len(self) != len(denominator):
0277             raise TypeError("Cannot divide %s with %i bins by "
0278                             "%s with %i bins." % 
0279                             (denominator.name, len(denominator), 
0280                              self.name, len(self)))
0281         quotient = copy.deepcopy(self)
0282         num_yerr = self.av_yerr()
0283         den_yerr = denominator.av_yerr()
0284         quotient.yerr = [0. for i in range(self.nbins)]
0285         for i in range(self.nbins):
0286             if denominator[i] == 0 or self[i] == 0:
0287                 quotient.y[i] = 0.
0288             else:
0289                 quotient.y[i] = self[i] / denominator[i]
0290                 quotient.yerr[i] = quotient[i]
0291                 quotient.yerr[i] *= math.sqrt((num_yerr[i] / self[i]) ** 2 +
0292                                        (den_yerr[i] / denominator[i]) ** 2)
0293             if quotient.yerr[i] > quotient[i]:
0294                 quotient.yerr[i] = quotient[i]
0295         quotient.yerr = [quotient.yerr, quotient.yerr]
0296         return quotient
0297     def divide_wilson(self, denominator):
0298         """Return an efficiency plot with Wilson score interval errors."""
0299         if len(self) != len(denominator):
0300             raise TypeError("Cannot divide %s with %i bins by "
0301                             "%s with %i bins." % 
0302                             (denominator.name, len(denominator), 
0303                              self.name, len(self)))
0304         eff, upper_err, lower_err = wilson_interval(self.y, denominator.y)
0305         quotient = copy.deepcopy(self)
0306         quotient.y = eff
0307         quotient.yerr = [lower_err, upper_err]
0308         return quotient
0309 
0310 class HistStack(object):
0311     """
0312     A container to hold Hist objects for plotting together.
0313 
0314     When plotting, the title and the x and y labels of the last Hist added
0315     will be used unless specified otherwise in the constructor.
0316     """
0317     def __init__(self, hists=None, title=None, xlabel=None, ylabel=None):
0318         self.hists  = []
0319         self.kwargs = []
0320         self.title  = title
0321         self.xlabel = xlabel
0322         self.ylabel = ylabel
0323         if hists:
0324             for hist in hists:
0325                 self.add(hist)
0326     def __getitem__(self, index):
0327         """Return indexth hist: x.__getitem__(y) <==> x[y]"""
0328         return self.hists[index]
0329     def __setitem__(self, index, value):
0330         """Replace indexth hist with value: x.__setitem__(i, y) <==> x[i]=y"""
0331         self.hists[index] = value
0332     def __len__(self):
0333         """Return the number of hists in the stack: x.__len__() <==> len(x)"""
0334         return len(self.hists)
0335     def __iter__(self):
0336         """Iterate through hists in the stack: x.__iter__() <==> iter(x)"""
0337         return iter(self.hists)
0338     def max(self):
0339         """Return the value of the highest bin of all hists in the stack."""
0340         maxes = [max(x) for x in self.hists]
0341         try:
0342             return max(maxes)
0343         except ValueError:
0344             return 0
0345     def stackmax(self):
0346         """Return the value of the highest bin in the addition of all hists."""
0347         try:
0348             return max([sum([h[i] for h in self.hists])
0349                        for i in range(self.hists[0].nbins)])
0350         except:
0351             print([h.nbins for h in self.hists])
0352     def scale(self, factor):
0353         """Scale all Hists by factor."""
0354         for hist in self.hists:
0355             hist.scale(factor)
0356     def min(self, threshold=None):
0357         """
0358         Return the value of the lowest bin of all hists in the stack.
0359 
0360         If threshold is specified, only values above the threshold will be
0361         considered.
0362         """
0363         mins = [x.min(threshold) for x in self.hists]
0364         return min(mins)
0365     def add(self, hist, **kwargs):
0366         """
0367         Add a Hist object to this stack.
0368 
0369         Any additional keyword arguments will be added to just this Hist
0370         when the stack is plotted.
0371         """
0372         if "label" in kwargs:
0373             hist.label = kwargs['label']
0374             del kwargs['label']
0375         if len(self) > 0:
0376             if hist.xedges != self.hists[0].xedges:
0377                 raise ValueError("Cannot add %s to stack; all Hists must "
0378                                  "have the same binning." % hist.name)
0379         self.hists.append(hist)
0380         self.kwargs.append(kwargs)
0381 
0382 
0383 ################ Define functions and classes for navigating within ROOT
0384 
0385 class RootFile(object):
0386     """A wrapper for TFiles, allowing easier access to methods."""
0387     def __init__(self, filename, name=None):
0388         self.filename = filename
0389         self.name = name or filename[:-5]
0390         self.file = ROOT.TFile(filename, 'read')
0391         if self.file.IsZombie():
0392             raise ValueError("Error opening %s" % filename)
0393     def cd(self, directory=''):
0394         """Make directory the current directory."""
0395         self.file.cd(directory)
0396     def get(self, object_name, path=None, type1D=Hist, type2D=Hist2D):
0397         """Return a Hist object from the given path within this file."""
0398         if not path:
0399             path = os.path.dirname(object_name)
0400             object_name = os.path.basename(object_name)
0401         try:
0402             roothist = self.file.GetDirectory(path).Get(object_name)
0403         except ReferenceError as e:
0404             raise ReferenceError(e)
0405         try:
0406             return type2D(roothist)
0407         except TypeError:
0408             return type1D(roothist)
0409 
0410 def ls(directory=None):
0411     """Return a python list of ROOT object names from the given directory."""
0412     if directory == None:
0413         keys = ROOT.gDirectory.GetListOfKeys()
0414     else:
0415         keys = ROOT.gDirectory.GetDirectory(directory).GetListOfKeys()
0416     key = keys[0]
0417     names = []
0418     while key:
0419         obj = key.ReadObj()
0420         key = keys.After(key)
0421         names.append(obj.GetName())
0422     return names
0423 
0424 def pwd():
0425     """Return ROOT's present working directory."""
0426     return ROOT.gDirectory.GetPath()
0427 
0428 def get(object_name):
0429     """Return a Hist object with the given name."""
0430     return Hist(ROOT.gDirectory.Get(object_name))
0431 
0432 
0433 ################ Define additional helping functions
0434 
0435 def loadROOT(batch=True):
0436     ## We need to temporarily change sys.argv so that ROOT doesn't intercept 
0437     ## options from the command-line
0438     saved_argv = sys.argv[:]
0439     argstring = ' '.join(sys.argv)
0440     sys.argv = [sys.argv[0]]
0441     try:
0442         import ROOT
0443     except ImportError:
0444         print("""\
0445 The program was unable to access PyROOT.  Usually, this just requires switching
0446 to the same major version of python that used when compiling ROOT.  To
0447 determine which version that is, try the following command:
0448     root -config 2>&1 | tr ' ' '\\n' | egrep 'python|PYTHON'
0449 If this is different from the python version you are currently using, try
0450 changing your PATH to point to the new one.""")
0451         sys.exit(1)
0452     ## Enter batch mode, unless outputting to C macros
0453     ## There is a bug in pyROOT that fails to export colors in batch mode
0454     if batch:
0455         ROOT.gROOT.SetBatch()
0456     ROOT.gErrorIgnoreLevel = ROOT.kWarning
0457     ## PyROOT picks up ~/.rootlogon if it exists, but not ./rootlogon.C 
0458     if os.path.exists('rootlogon.C'):
0459         ROOT.gROOT.Macro('rootlogon.C')
0460     sys.argv = saved_argv[:]
0461     return ROOT
0462 
0463 def replace(string, replacements):
0464     """
0465     Modify a string based on a list of patterns and substitutions.
0466 
0467     replacements should be a list of two-entry tuples, the first entry giving
0468     a string to search for and the second entry giving the string with which
0469     to replace it.  If replacements includes a pattern entry containing
0470     'use_regexp', then all patterns will be treated as regular expressions
0471     using re.sub.
0472     """
0473     if not replacements:
0474         return string
0475     if 'use_regexp' in [x for x,y in replacements]:
0476         for pattern, repl in [x for x in replacements
0477                               if x[0] != 'use_regexp']:
0478             string = re.sub(pattern, repl, string)
0479     else:
0480         for pattern, repl in replacements:
0481             string = string.replace(pattern, repl)
0482     if re.match(_all_whitespace_string, string):
0483         return ""
0484     return string
0485 
0486 def process_bin_labels(binlabels):
0487     has_labels = False
0488     for binlabel in binlabels:
0489         if binlabel:
0490             has_labels = True
0491     if has_labels:
0492         return binlabels
0493     else:
0494         return None
0495 
0496 def wilson_interval(numerator_array, denominator_array):
0497     eff, upper_err, lower_err = [], [], []
0498     for n, d in zip(numerator_array, denominator_array):
0499         try:
0500             p = float(n) / d
0501             s = math.sqrt(p * (1 - p) / d + 1 / (4 * d * d))
0502             t = p + 1 / (2 * d)
0503             eff.append(p)
0504             upper_err.append(-p + 1/(1 + 1/d) * (t + s))
0505             lower_err.append(+p - 1/(1 + 1/d) * (t - s))
0506         except ZeroDivisionError:
0507             eff.append(0)
0508             upper_err.append(0)
0509             lower_err.append(0)
0510     return eff, upper_err, lower_err
0511 
0512 def find_num_processors():
0513     import os
0514     try:
0515         num_processors = os.sysconf('SC_NPROCESSORS_ONLN')
0516     except:
0517         try:
0518             num_processors = os.environ['NUMBER_OF_PROCESSORS']
0519         except:
0520             num_processors = 1
0521     return num_processors
0522 
0523 def testfile():
0524     outfile = ROOT.TFile("test.root", "recreate")
0525     for i in range(4):
0526         d = outfile.mkdir("dir%i" % (i + 1))
0527         d.cd()
0528         for j in range(4):
0529             hist = ROOT.TH1F("hist%i" % (j + 1), "A Histogram", 10, 0, 10)
0530             hist.Fill(j)
0531             hist.Write()
0532     outfile.Write()
0533     return outfile
0534 
0535 #### Functions for globbing within root files
0536 
0537 glob_magic_check = re.compile('[*?[]')
0538 
0539 def has_glob_magic(s):
0540     return glob_magic_check.search(s) is not None
0541 
0542 # These 2 helper functions non-recursively glob inside a literal directory.
0543 # They return a list of basenames. `_rootglob1` accepts a pattern while 
0544 # `_rootglob0` takes a literal basename (so it only has to check for its 
0545 # existence).
0546 
0547 def _rootglob1(tdirectory, dirname, pattern):
0548     if not tdirectory.GetDirectory(dirname):
0549         return []
0550     names = [key.GetName() for key in 
0551              tdirectory.GetDirectory(dirname).GetListOfKeys()]
0552     return fnmatch.filter(names, pattern)
0553 
0554 def _rootglob0(tdirectory, dirname, basename):
0555     if tdirectory.Get(os.path.join(dirname, basename)):
0556         return [basename]
0557     return []
0558 
0559 def rootglob(tdirectory, pathname):
0560     """Return a list of paths matching a pathname pattern.
0561 
0562     The pattern may contain simple shell-style wildcards a la fnmatch.
0563 
0564     >>> import rootplot.utilities
0565     >>> f = rootplot.utilities.testfile()
0566     >>> rootglob(f, '*')
0567     ['dir1', 'dir2', 'dir3', 'dir4']
0568     >>> rootglob(f, 'dir1/*')
0569     ['dir1/hist1', 'dir1/hist2', 'dir1/hist3', 'dir1/hist4']
0570     >>> rootglob(f, '*/hist1')
0571     ['dir1/hist1', 'dir2/hist1', 'dir3/hist1', 'dir4/hist1']
0572     >>> rootglob(f, 'dir1/hist[1-2]')
0573     ['dir1/hist1', 'dir1/hist2']
0574     """
0575     return list(irootglob(tdirectory, pathname))
0576 
0577 def irootglob(tdirectory, pathname):
0578     """Return an iterator which yields the paths matching a pathname pattern.
0579 
0580     The pattern may contain simple shell-style wildcards a la fnmatch.
0581 
0582     """
0583     if not has_glob_magic(pathname):
0584         if tdirectory.Get(pathname):
0585             yield pathname
0586         return
0587     dirname, basename = os.path.split(pathname)
0588     if has_glob_magic(dirname):
0589         dirs = irootglob(tdirectory, dirname)
0590     else:
0591         dirs = [dirname]
0592     if has_glob_magic(basename):
0593         glob_in_dir = _rootglob1
0594     else:
0595         glob_in_dir = _rootglob0
0596     for dirname in dirs:
0597         for name in glob_in_dir(tdirectory, dirname, basename):
0598             yield os.path.join(dirname, name)
0599 
0600 if __name__ == '__main__':
0601     import doctest
0602     doctest.testmod()