CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_5/src/FWCore/ParameterSet/python/TreeCrawler.py

Go to the documentation of this file.
00001 # This CMS code is based on previous work done by Toby Dickenson, as indiciated below
00002 #
00003 # for questions: Benedikt.Hegner@cern.ch
00004 
00005 # Copyright 2004 Toby Dickenson
00006 #
00007 # Permission is hereby granted, free of charge, to any person obtaining
00008 # a copy of this software and associated documentation files (the
00009 # "Software"), to deal in the Software without restriction, including
00010 # without limitation the rights to use, copy, modify, merge, publish,
00011 # distribute, sublicense, and/or sell copies of the Software, and to
00012 # permit persons to whom the Software is furnished to do so, subject
00013 # to the following conditions:
00014 #
00015 # The above copyright notice and this permission notice shall be included
00016 # in all copies or substantial portions of the Software.
00017 #
00018 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00019 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00020 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00021 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
00022 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
00023 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
00024 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00025 
00026 import sys, os, inspect, copy
00027 import modulefinder
00028 
00029 def packageNameFromFilename(name):
00030     return ".".join(name.replace("python/","").replace(".py","").split("/")[-3:])
00031 
00032 
00033 class Color:
00034   """ANSI escape display sequences"""
00035   info          = "\033[1;34m"
00036   hilight       = "\033[31m"
00037   alternate     = "\033[32m"
00038   extra         = "\033[33m"
00039   backlight     = "\033[43m"
00040   underline     = "\033[4m"
00041   lessemphasis  = "\033[30m"
00042   deemphasis    = "\033[1;30m"
00043   none          = "\033[0m"
00044 
00045 _stack = []
00046 
00047 class SearchHit:
00048     pass
00049 
00050 class Package(object):
00051     def __init__(self,name,top=False):
00052         self.name = name
00053         self.dependencies = []
00054         self.searched = False
00055         self.stack = []
00056         if top:
00057             self.module = None
00058         else:    
00059             self.module = __import__(name,[],[],"*")
00060     def dump(self,level):
00061         indent = "  " * level
00062         print indent, "+", Color.info, self.name, Color.none
00063         # sort dependencies alphabetically
00064         self.dependencies.sort(key = lambda x: x.name)
00065         for package in self.dependencies:
00066             package.dump(level+1)
00067     def search(self,pattern,result):
00068         """ recursive search for pattern in source files"""
00069         # first start searching in the package itself / do this only once
00070         if self.module:
00071             for number, line in enumerate(inspect.getsource(self.module).splitlines()):
00072                 if pattern in line:
00073                      filename = packageNameFromFilename(inspect.getsourcefile(self.module))
00074                      if not self.searched:
00075                          # save the hit, so we can add later stacks to it
00076                          self.hit = SearchHit()
00077                          self.hit.number = number
00078                          self.hit.filename = filename
00079                          self.hit.line = line
00080                          self.hit.stacks = list()
00081                          result.append(self.hit)
00082                      self.hit.stacks.append(copy.copy(_stack)) 
00083         # then go on with dependencies
00084         _stack.append(self.name)
00085         for package in self.dependencies:
00086             package.search(pattern,result)
00087         _stack.pop() 
00088         self.searched = True    
00089 
00090 
00091 class mymf(modulefinder.ModuleFinder):
00092     def __init__(self,*args,**kwargs):
00093         self._depgraph = {}
00094         self._types = {}
00095         self._last_caller = None
00096         #TODO - replace by environment variables CMSSW_BASE and CMSSW_RELEASE_BASE (*and* do it only if the global one is not empty like for IB areas)  
00097         self._localarea = os.path.expandvars('$CMSSW_BASE')
00098         self._globalarea = os.path.expandvars('$CMSSW_RELEASE_BASE')
00099         modulefinder.ModuleFinder.__init__(self,*args,**kwargs)
00100     def import_hook(self, name, caller=None, fromlist=None, level=-1):
00101         old_last_caller = self._last_caller
00102         try:
00103             self._last_caller = caller
00104             return modulefinder.ModuleFinder.import_hook(self,name,caller,fromlist, level=level)  
00105         finally:
00106             self._last_caller = old_last_caller
00107 
00108     def import_module(self,partnam,fqname,parent):
00109                               
00110         if partnam in ("FWCore","os"):
00111             r = None
00112         else:
00113             r = modulefinder.ModuleFinder.import_module(self,partnam,fqname,parent)
00114             # since the modulefinder is not able to look into the global area when coming from the local area, we force a second try   
00115             if parent and not r and self._localarea != '' and self._globalarea != '':
00116                  parent.__file__ = parent.__file__.replace(self._localarea,self._globalarea)
00117                  parent.__path__[0] = parent.__path__[0].replace(self._localarea,self._globalarea)
00118             r = modulefinder.ModuleFinder.import_module(self,partnam,fqname,parent)
00119                                                          
00120         if r is not None:
00121             self._depgraph.setdefault(self._last_caller.__name__,{})[r.__name__] = 1
00122         return r
00123     def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
00124         r = modulefinder.ModuleFinder.load_module(self, fqname, fp, pathname, (suffix, mode, type))
00125         if r is not None:
00126             self._types[r.__name__] = type
00127         return r
00128 
00129 
00130 def transformIntoGraph(depgraph,toplevel):
00131     packageDict = {}
00132     # create the top level config
00133     packageDict[toplevel] = Package(toplevel, top = True) 
00134 
00135     # create package objects
00136     for key, value in depgraph.iteritems():
00137         if key.count(".") == 2 and key != toplevel: 
00138             packageDict[key] = Package(key)
00139         for name in value.keys():
00140             if name.count(".") == 2: packageDict[name] = Package(name)
00141     # now create dependencies
00142     for key, value in depgraph.iteritems():
00143         if key.count(".") == 2 or key == toplevel:
00144             package = packageDict[key]
00145             package.dependencies = [packageDict[name] for name in value.keys() if name.count(".") == 2]
00146 
00147     # find and return the top level config
00148     return packageDict[toplevel]
00149 
00150 
00151 def getDependenciesFromPythonFile(filename,toplevelname,path):
00152     modulefinder = mymf(path)
00153     modulefinder.run_script(filename)
00154     globalDependencyDict = modulefinder._depgraph
00155     globalDependencyDict[toplevelname] = globalDependencyDict["__main__"] 
00156     return globalDependencyDict
00157 
00158 
00159 def getImportTree(filename,path):
00160     toplevelname = packageNameFromFilename(filename)
00161     # get dependencies from given file
00162     globalDependencyDict = getDependenciesFromPythonFile(filename,toplevelname,path)
00163         
00164     # transform this flat structure in a dependency tree
00165     dependencyGraph = transformIntoGraph(globalDependencyDict,toplevelname)
00166     return dependencyGraph