Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-11-25 02:29:52

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