Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
#! /usr/bin/env python3

from builtins import object
import os
import sys
import copy
from argparse import ArgumentParser
import re
import copy
import subprocess

# define regex
wrapperRE      = re.compile (r'edm::Wrapper<(.+)\s*>$')
trailingDotRE  = re.compile (r'\.$')
underscoreRE   = re.compile (r'_')
leadingSlashRE = re.compile (r'^/')
webRE          = re.compile (r'^\w+://')

def getOutput (command):
    '''Given an input command, returns STDOUT and STDERR'''
    proc = subprocess.Popen(command,
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE
                            )
    return proc.communicate()


def expandFilename (filename, options):
    '''Intelligently guesses as to whether or not it is given an PFN
    or LFN.  Returns PFN.'''
    # if this is a web address, don't modify it
    if webRE.search (filename):
        return filename
    # if it doesn't start with a slash, return it as is
    if not leadingSlashRE.search (filename):
        return filename
    # if the file exists locally AND 'lfn' option is not given, return
    # as is
    if not options.lfn and os.path.exists (filename):
        return filename
    # OK.  If we're here, then we probably have an LFN.  Convert it
    stdout, stderr = getOutput ('edmFileUtil -d %s' % filename)
    if stdout:
        return stdout.strip()
    raise RuntimeError("Could not successfully run 'edmFileUtil.' Error %s" \
          % stderr)


class Branch (object):
    # default options
    form = "%(type)5s  %(module)5s  %(label)5s  %(process)5s"
    mode = 'normal'
    forceColumns = False
    inQuotes = ['module', 'label', 'process']

    def __init__ (self, branchInfo, regexList = None):
        """Takes the needed information from Root's Branch Info"""
        match = wrapperRE.match( branchInfo.GetTypeName() )
        if match:
            self.type = match.group (1)
        else:
            # this isn't a type we're interested in
            raise ValueError("Not edm::Wrapper")
        name = trailingDotRE.sub ('', branchInfo.GetName())
        pieces = underscoreRE.split (name)
        if len (pieces) != 4:
            raise ValueError("%s not formatted as expected" % name)
        self.typeString = pieces[0] # not currently used
        self.module     = pieces[1]
        self.label      = pieces[2]
        self.process    = pieces[3]
        self.fullname   = "_".join (pieces)
        self.splitlevel = branchInfo.GetSplitLevel()
        # if we're past a regex list, then make sure we've got a match
        if not regexList:
            # nothing more to do
            return
        found = False
        for regex in regexList:
            # search the branch name
            if regex.search (self.fullname):
                found = True
                break
            if regex.search (self.type):
                found = True
                break
        if not found:
            raise ValueError("%s does not match regexs provided" \
                  % self.fullname)

        
    def __str__ (self):
        """String representation """
        copyDict = copy.copy (self.__dict__)
        for key in Branch.inQuotes:
            copyDict[key] = '"%s"' % copyDict[key]
        return Branch.form % copyDict
    
    
    @staticmethod
    def _setForm (branchList):
        '''Loop through lists and set widths and form appropriately'''
        # each item in this dictionary is a variable in Branch where
        # we want to keep track of the length.  The first number in
        # the list is the maximum length of all of the Branch items in
        # the list.  The second entry is the maximum length that it
        # should take IFF we are enforcing maximum length.
        lengthDict = { 'type'    : [7, 35],
                       'module'  : [7, 25],
                       'label'   : [7, 15],
                       'process' : [7, 15],
                       'fullname': [7, 50]}
        # Since we want this script to print out the columns in the
        # order that we want and not some random order, we specify
        # that order here.
        order = ['type', 'module', 'label', 'process']
        for obj in branchList:
            for key, twoList in lengthDict.items():
                attribute = getattr (obj, key)
                if len (attribute) + 2 > twoList[0]:
                    twoList[0] = len (attribute) + 2
        totalLength = 0
        for twoList in list(lengthDict.values()):
            totalLength += twoList[0]
        useMin = True
        if Branch.forceColumns or totalLength < 70:
            useMin = False
        Branch.form = ""
        if Branch.mode == 'name':
            # name
            Branch.form += '%(fullname)s'
        else:
            for piece in order:
                Branch.form += '%('
                Branch.form +=  piece
                if useMin:
                    Branch.form += ')-%ds   ' % min ( lengthDict[piece] )
                else:
                    Branch.form += ')-%ds   ' % lengthDict[piece][0]
        if Branch.mode == 'all':
            # name
            Branch.form += '     %('
            if useMin:
                Branch.form += 'fullname)-%ds' % min ( lengthDict['fullname'] )
            else:
                Branch.form += 'fullname)-%ds' % lengthDict['fullname'][0]
            # split level
            Branch.form += '     %(splitlevel)s'


    @staticmethod
    def printList (branchList):
        '''Print out nicely formatted list of all branches'''
        Branch._setForm (branchList)
        title = Branch.form % {'type'     : 'Type',
                               'module'   : 'Module',
                               'label'    : 'Label',
                               'process'  : 'Process',
                               'fullname' : 'Full Name',
                               'splitlevel' : 'Split level'}
        print(title)
        print('-' * len(title))
        for branch in branchList:
            print(branch)

#branches = ''
if __name__ == "__main__":

    parser = ArgumentParser(description="Prints out info on edm file.")
    nameAllGroup = parser.add_mutually_exclusive_group()
    nameAllGroup.add_argument('--name', dest='name', action='store_true',
                              help='print out only branch names')
    nameAllGroup.add_argument('--all',  dest='all',  action='store_true',
                              help='Print out everything: type, module, label, '\
                              'process, branch name, and branch split level')
    parser.add_argument('--lfn', dest='lfn', action='store_true',
                        help="Force LFN2PFN translation (usually not necessary)")
    lumiRunGroup = parser.add_mutually_exclusive_group()
    lumiRunGroup.add_argument('--lumi', dest='lumi', action='store_true',
                              help="Look at 'lumi' tree")
    lumiRunGroup.add_argument('--run', dest='run', action='store_true',
                              help="Look at 'run' tree")
    parser.add_argument("--regex", dest='regex', action="append",
                        type=str, default=[],
                        help="Filter results based on regex")
    parser.add_argument('--skipping', dest='skipping', action='store_true',
                        help="Print out branches being skipped")
    parser.add_argument('--forceColumns', dest='forceColumns',
                        action='store_true',
                        help="Forces printouts to be in nice columns")
    parser.add_argument("templates", metavar="templates.root", type=str)
    options = parser.parse_args()
    filename = expandFilename (options.templates, options)
    ###################
    # process options #
    ###################
    if options.name:
        Branch.mode = 'name'
    elif options.all:
        Branch.mode = 'all'
    Branch.forceColumns = options.forceColumns

    treeName = 'Events'
    if options.lumi:
        treeName = 'LuminosityBlocks'
    elif options.run:
        treeName = 'Runs'
    regexList = []
    # are we asked to filter this list?
    for regexString in options.regex:
        regexList.append( re.compile( regexString, re.IGNORECASE ) )
    # Because PyRoot is, well, greedy, we want to make sure we have
    # setup and parsed the command line options before importing ROOT
    # otherwise ROOT will try and do this for us.
    import ROOT
    ROOT.gROOT.SetBatch() # setting batch mode
    # Here we turn off stderr so that we don't get all of the errors
    # saying we don't have a dictionary for all of the objects in the
    # root file.  When we've loaded the file, we turn it back on
    oldStderr = os.dup( sys.stderr.fileno() )
    newStderr = open ( '/dev/null', 'w')
    os.dup2( newStderr.fileno(), sys.stderr.fileno() )
    rootfile = ROOT.TFile.Open( filename )
    os.dup2( oldStderr, sys.stderr.fileno() )
    if not rootfile:
        raise RuntimeError("Could not open file '%s'." % filename)
    Tree = rootfile.Get (treeName)
    branches = Tree.GetListOfBranches()
    branchList = []
    for branchInfo in branches:
        try:
            branch = Branch (branchInfo, regexList)
        except Exception as detail:
            if options.skipping:
                print("Skipping ", detail)
            continue
        branchList.append (branch)
    Branch.printList (branchList)
    rootfile.Close()
    del rootfile