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
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
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
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
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
0433
0434 def loadROOT(batch=True):
0435
0436
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
0452
0453 if batch:
0454 ROOT.gROOT.SetBatch()
0455 ROOT.gErrorIgnoreLevel = ROOT.kWarning
0456
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
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
0542
0543
0544
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()