CMS 3D CMS Logo

List of all members | Public Member Functions | Private Attributes
TreeCrawler.mymf Class Reference
Inheritance diagram for TreeCrawler.mymf:

Public Member Functions

def __init__ (self, *args, **kwargs)
 
def import_hook (self, name, caller=None, fromlist=None, level=-1)
 
def import_module (self, partnam, fqname, parent)
 
def load_module (self, fqname, fp, pathname, aux_info)
 
def scan_opcodes_25 (self, co, unpack=struct.unpack)
 

Private Attributes

 _depgraph
 
 _globalarea
 
 _last_caller
 
 _localarea
 
 _types
 

Detailed Description

Definition at line 94 of file TreeCrawler.py.

Constructor & Destructor Documentation

◆ __init__()

def TreeCrawler.mymf.__init__ (   self,
args,
**  kwargs 
)

Definition at line 95 of file TreeCrawler.py.

95  def __init__(self,*args,**kwargs):
96  self._depgraph = {}
97  self._types = {}
98  self._last_caller = None
99  #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)
100  self._localarea = os.path.expandvars('$CMSSW_BASE')
101  self._globalarea = os.path.expandvars('$CMSSW_RELEASE_BASE')
102  modulefinder.ModuleFinder.__init__(self,*args,**kwargs)

Member Function Documentation

◆ import_hook()

def TreeCrawler.mymf.import_hook (   self,
  name,
  caller = None,
  fromlist = None,
  level = -1 
)

Definition at line 103 of file TreeCrawler.py.

103  def import_hook(self, name, caller=None, fromlist=None, level=-1):
104  old_last_caller = self._last_caller
105  try:
106  self._last_caller = caller
107  return modulefinder.ModuleFinder.import_hook(self,name,caller,fromlist, level=level)
108  finally:
109  self._last_caller = old_last_caller
110 

References TreeCrawler.mymf._last_caller.

◆ import_module()

def TreeCrawler.mymf.import_module (   self,
  partnam,
  fqname,
  parent 
)

Definition at line 111 of file TreeCrawler.py.

111  def import_module(self,partnam,fqname,parent):
112 
113  if partnam in ("os","unittest"):
114  r = None
115  else:
116  r = modulefinder.ModuleFinder.import_module(self,partnam,fqname,parent)
117  # since the modulefinder is not able to look into the global area when coming from the local area, we force a second try
118  if parent and not r and self._localarea != '' and self._globalarea != '':
119  parent.__file__ = parent.__file__.replace(self._localarea,self._globalarea)
120  parent.__path__[0] = parent.__path__[0].replace(self._localarea,self._globalarea)
121  r = modulefinder.ModuleFinder.import_module(self,partnam,fqname,parent)
122 
123  if r is not None:
124  self._depgraph.setdefault(self._last_caller.__name__,{})[r.__name__] = 1
125  return r

References TreeCrawler.mymf._depgraph, TreeCrawler.mymf._globalarea, TreeCrawler.mymf._last_caller, TreeCrawler.mymf._localarea, and python.rootplot.root2matplotlib.replace().

◆ load_module()

def TreeCrawler.mymf.load_module (   self,
  fqname,
  fp,
  pathname,
  aux_info 
)

Definition at line 126 of file TreeCrawler.py.

126  def load_module(self, fqname, fp, pathname, aux_info):
127  (suffix, mode, type) = aux_info
128  r = modulefinder.ModuleFinder.load_module(self, fqname, fp, pathname, (suffix, mode, type))
129  if r is not None:
130  self._types[r.__name__] = type
131  return r
132 

References TreeCrawler.mymf._types, and hcaldqm::quantity::EventType._types.

◆ scan_opcodes_25()

def TreeCrawler.mymf.scan_opcodes_25 (   self,
  co,
  unpack = struct.unpack 
)
This is basically just the default opcode scanner from ModuleFinder, but extended to also
look for "process.load(<module>)' commands. Since the Process object might not necassarily
be called "process", it scans for a call to a "load" method with a single parameter on
*any* object. If one is found it checks if the parameter is a string that refers to a valid
python module in the local or global area. If it does, the scanner assumes this was a call
to a Process object and yields the module name.
It's not possible to scan first for Process object declarations to get the name of the
objects since often (e.g. for customisation functions) the object is passed to a function
in a different file.

The ModuleFinder.scan_opcodes_25 implementation this is based was taken from
https://hg.python.org/cpython/file/2.7/Lib/modulefinder.py#l364

Definition at line 133 of file TreeCrawler.py.

133  def scan_opcodes_25(self, co, unpack = struct.unpack):
134  """
135  This is basically just the default opcode scanner from ModuleFinder, but extended to also
136  look for "process.load(<module>)' commands. Since the Process object might not necassarily
137  be called "process", it scans for a call to a "load" method with a single parameter on
138  *any* object. If one is found it checks if the parameter is a string that refers to a valid
139  python module in the local or global area. If it does, the scanner assumes this was a call
140  to a Process object and yields the module name.
141  It's not possible to scan first for Process object declarations to get the name of the
142  objects since often (e.g. for customisation functions) the object is passed to a function
143  in a different file.
144 
145  The ModuleFinder.scan_opcodes_25 implementation this is based was taken from
146  https://hg.python.org/cpython/file/2.7/Lib/modulefinder.py#l364
147  """
148  # Scan the code, and yield 'interesting' opcode combinations
149  # Python 2.5 version (has absolute and relative imports)
150  code = co.co_code
151  names = co.co_names
152  consts = co.co_consts
153  LOAD_CONST = modulefinder.LOAD_CONST
154  IMPORT_NAME = modulefinder.IMPORT_NAME
155  STORE_OPS = modulefinder.STORE_OPS
156  HAVE_ARGUMENT = modulefinder.HAVE_ARGUMENT
157  LOAD_ATTR = chr(dis.opname.index('LOAD_ATTR'))
158  LOAD_NAME = chr(dis.opname.index('LOAD_NAME'))
159  CALL_FUNCTION = chr(dis.opname.index('CALL_FUNCTION'))
160  LOAD_LOAD_AND_IMPORT = LOAD_CONST + LOAD_CONST + IMPORT_NAME
161 
162  try :
163  indexOfLoadConst = names.index("load") # This might throw a ValueError
164  # These are the opcodes required to access the "load" attribute. This might
165  # not even be a function, but I check for that later.
166  loadMethodOpcodes = LOAD_ATTR+struct.pack('<H',indexOfLoadConst)
167  except ValueError :
168  # doesn't look like "load" is used anywhere in this file
169  loadMethodOpcodes=None
170 
171  while code:
172  c = code[0]
173 
174  # Check to see if this is a call to a "load" method
175  if loadMethodOpcodes!=None and len(code)>=9 : # Need at least 9 codes for the full call
176  if code[:3]==loadMethodOpcodes :
177  # The attribute "load" is being accessed, need to make sure this is a function call.
178  # I'll look ahead and see if the CALL_FUNCTION code is used - this could be in a different
179  # place depending on the number of arguments, but I'm only interested in methods with a
180  # single argument so I know exactly where CALL_FUNCTION should be.
181  if code[6]==CALL_FUNCTION :
182  # I know this is calling a method called "load" with one argument. I need
183  # to find out what the argument is. Note that I still don't know if this is
184  # on a cms.Process object.
185  indexInTable=unpack('<H',code[4:6])[0]
186  if code[3]==LOAD_CONST :
187  # The argument is a constant, so retrieve that from the table
188  loadMethodArgument=consts[indexInTable]
189  # I know a load method with one argument has been called on *something*, but I don't
190  # know if it was a cms.Process object. All I can do is check to see if the argument is
191  # a string, and if so if it refers to a python file in the user or global areas.
192  try :
193  loadMethodArgument = loadMethodArgument.replace("/",".")
194  # I can only use imp.find_module on submodules (i.e. each bit between a "."), so try
195  # that on each submodule in turn using the previously found filename. Note that I have
196  # to try this twice, because if the first pass traverses into a package in the local
197  # area but the subpackage has not been checked out it will report that the subpackage
198  # doesn't exist, even though it is available in the global area.
199  try :
200  parentFilename=[self._localarea+"/python"]
201  for subModule in loadMethodArgument.split(".") :
202  moduleInfo=imp.find_module( subModule, parentFilename )
203  parentFilename=[moduleInfo[1]]
204  # If control got this far without raising an exception, then it must be a valid python module
205  yield "import", (None, loadMethodArgument)
206  except ImportError :
207  # Didn't work in the local area, try in the global area.
208  parentFilename=[self._globalarea+"/python"]
209  for subModule in loadMethodArgument.split(".") :
210  moduleInfo=imp.find_module( subModule, parentFilename )
211  parentFilename=[moduleInfo[1]]
212  # If control got this far without raising an exception, then it must be a valid python module
213  yield "import", (None, loadMethodArgument)
214  except Exception as error:
215  # Either there was an import error (not a python module) or there was a string
216  # manipulaton error (argument not a string). Assume this wasn't a call on a
217  # cms.Process object and move on silently.
218  pass
219 
220  elif code[3]==LOAD_NAME :
221  # The argument is a variable. I can get the name of the variable quite easily but
222  # not the value, unless I execute all of the opcodes. Not sure what to do here,
223  # guess I'll just print a warning so that the user knows?
224  print("Unable to determine the value of variable '"+names[indexInTable]+"' to see if it is a proces.load(...) statement in file "+co.co_filename)
225 
226  code=code[9:]
227  continue
228 
229  if c in STORE_OPS:
230  oparg, = unpack('<H', code[1:3])
231  yield "store", (names[oparg],)
232  code = code[3:]
233  continue
234  if code[:9:3] == LOAD_LOAD_AND_IMPORT:
235  oparg_1, oparg_2, oparg_3 = unpack('<xHxHxH', code[:9])
236  level = consts[oparg_1]
237  if level == -1: # normal import
238  yield "import", (consts[oparg_2], names[oparg_3])
239  elif level == 0: # absolute import
240  yield "absolute_import", (consts[oparg_2], names[oparg_3])
241  else: # relative import
242  yield "relative_import", (level, consts[oparg_2], names[oparg_3])
243  code = code[9:]
244  continue
245  if c >= HAVE_ARGUMENT:
246  code = code[3:]
247  else:
248  code = code[1:]
249 

References TreeCrawler.mymf._globalarea, TreeCrawler.mymf._localarea, edm.print(), and SiPixelPI.unpack().

Member Data Documentation

◆ _depgraph

TreeCrawler.mymf._depgraph
private

Definition at line 96 of file TreeCrawler.py.

Referenced by TreeCrawler.mymf.import_module().

◆ _globalarea

TreeCrawler.mymf._globalarea
private

◆ _last_caller

TreeCrawler.mymf._last_caller
private

Definition at line 98 of file TreeCrawler.py.

Referenced by TreeCrawler.mymf.import_hook(), and TreeCrawler.mymf.import_module().

◆ _localarea

TreeCrawler.mymf._localarea
private

◆ _types

TreeCrawler.mymf._types
private
SiPixelPI::unpack
std::pair< unsigned int, unsigned int > unpack(cond::Time_t since)
Definition: SiPixelPayloadInspectorHelper.h:44
edm::print
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
python.rootplot.root2matplotlib.replace
def replace(string, replacements)
Definition: root2matplotlib.py:444