Package madgraph :: Package various :: Module misc
[hide private]
[frames] | no frames]

Source Code for Module madgraph.various.misc

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15   
  16  """A set of functions performing routine administrative I/O tasks.""" 
  17   
  18  import contextlib 
  19  import logging 
  20  import os 
  21  import re 
  22  import signal 
  23  import subprocess 
  24  import sys 
  25  import StringIO 
  26  import sys 
  27  import optparse 
  28  import time 
  29  import shutil 
  30  import traceback 
  31  import gzip as ziplib 
  32   
  33  try: 
  34      # Use in MadGraph 
  35      import madgraph 
  36  except Exception, error: 
  37      # Use in MadEvent 
  38      import internal 
  39      from internal import MadGraph5Error, InvalidCmd 
  40      import internal.files as files 
  41      MADEVENT = True     
  42  else: 
  43      from madgraph import MadGraph5Error, InvalidCmd 
  44      import madgraph.iolibs.files as files 
  45      MADEVENT = False 
  46   
  47   
  48       
  49  logger = logging.getLogger('cmdprint.ext_program') 
  50  logger_stderr = logging.getLogger('madevent.misc') 
  51  pjoin = os.path.join 
52 53 #=============================================================================== 54 # Return a warning (if applicable) on the consistency of the current Pythia8 55 # and MG5_aMC version specified. It is placed here because it should be accessible 56 # from both madgraph5_interface and madevent_interface 57 #=============================================================================== 58 -def mg5amc_py8_interface_consistency_warning(options):
59 """ Check the consistency of the mg5amc_py8_interface installed with 60 the current MG5 and Pythia8 versions. """ 61 62 return None 63 # All this is only relevant is Pythia8 is interfaced to MG5 64 if not options['pythia8_path']: 65 return None 66 67 if not options['mg5amc_py8_interface_path']: 68 return \ 69 """ 70 A Pythia8 path is specified via the option 'pythia8_path' but no path for option 71 'mg5amc_py8_interface_path' is specified. This means that Pythia8 cannot be used 72 leading order simulations with MadEvent. 73 Consider installing the MG5_aMC-PY8 interface with the following command: 74 MG5_aMC>install mg5amc_py8_interface 75 """ 76 77 # Retrieve all the on-install and current versions 78 MG5_version_on_install = open(pjoin(options['mg5amc_py8_interface_path'], 79 'MG5AMC_VERSION_ON_INSTALL')).read().replace('\n','') 80 if MG5_version_on_install == 'UNSPECIFIED': 81 MG5_version_on_install = None 82 PY8_version_on_install = open(pjoin(options['mg5amc_py8_interface_path'], 83 'PYTHIA8_VERSION_ON_INSTALL')).read().replace('\n','') 84 MG5_curr_version = get_pkg_info()['version'] 85 try: 86 p = subprocess.Popen(['./get_pythia8_version.py',options['pythia8_path']], 87 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 88 cwd=options['mg5amc_py8_interface_path']) 89 (out, err) = p.communicate() 90 out = out.replace('\n','') 91 PY8_curr_version = out 92 # In order to test that the version is correctly formed, we try to cast 93 # it to a float 94 float(out) 95 except: 96 PY8_curr_version = None 97 98 if not MG5_version_on_install is None and not MG5_curr_version is None: 99 if MG5_version_on_install != MG5_curr_version: 100 return \ 101 """ 102 The current version of MG5_aMC (v%s) is different than the one active when 103 installing the 'mg5amc_py8_interface_path' (which was MG5aMC v%s). 104 Please consider refreshing the installation of this interface with the command: 105 MG5_aMC>install mg5amc_py8_interface 106 """%(MG5_curr_version, MG5_version_on_install) 107 108 if not PY8_version_on_install is None and not PY8_curr_version is None: 109 if PY8_version_on_install != PY8_curr_version: 110 return \ 111 """ 112 The current version of Pythia8 (v%s) is different than the one active when 113 installing the 'mg5amc_py8_interface' tool (which was Pythia8 v%s). 114 Please consider refreshing the installation of this interface with the command: 115 MG5_aMC>install mg5amc_py8_interface 116 """%(PY8_curr_version,PY8_version_on_install) 117 118 return None
119
120 121 122 #=============================================================================== 123 # parse_info_str 124 #=============================================================================== 125 -def parse_info_str(fsock):
126 """Parse a newline separated list of "param=value" as a dictionnary 127 """ 128 129 info_dict = {} 130 pattern = re.compile("(?P<name>\w*)\s*=\s*(?P<value>.*)", 131 re.IGNORECASE | re.VERBOSE) 132 for entry in fsock: 133 entry = entry.strip() 134 if len(entry) == 0: continue 135 m = pattern.match(entry) 136 if m is not None: 137 info_dict[m.group('name')] = m.group('value') 138 else: 139 raise IOError, "String %s is not a valid info string" % entry 140 141 return info_dict
142
143 144 #=============================================================================== 145 # mute_logger (designed to be a decorator) 146 #=============================================================================== 147 -def mute_logger(names=['madgraph','ALOHA','cmdprint','madevent'], levels=[50,50,50,50]):
148 """change the logger level and restore those at their initial value at the 149 end of the function decorated.""" 150 def control_logger(f): 151 def restore_old_levels(names, levels): 152 for name, level in zip(names, levels): 153 log_module = logging.getLogger(name) 154 log_module.setLevel(level)
155 156 def f_with_no_logger(self, *args, **opt): 157 old_levels = [] 158 for name, level in zip(names, levels): 159 log_module = logging.getLogger(name) 160 old_levels.append(log_module.level) 161 log_module.setLevel(level) 162 try: 163 out = f(self, *args, **opt) 164 restore_old_levels(names, old_levels) 165 return out 166 except: 167 restore_old_levels(names, old_levels) 168 raise 169 170 return f_with_no_logger 171 return control_logger 172
173 #=============================================================================== 174 # get_pkg_info 175 #=============================================================================== 176 -def get_pkg_info(info_str=None):
177 """Returns the current version information of the MadGraph5_aMC@NLO package, 178 as written in the VERSION text file. If the file cannot be found, 179 a dictionary with empty values is returned. As an option, an info 180 string can be passed to be read instead of the file content. 181 """ 182 183 if info_str: 184 info_dict = parse_info_str(StringIO.StringIO(info_str)) 185 186 elif MADEVENT: 187 info_dict ={} 188 info_dict['version'] = open(pjoin(internal.__path__[0],'..','..','MGMEVersion.txt')).read().strip() 189 info_dict['date'] = '20xx-xx-xx' 190 else: 191 info_dict = files.read_from_file(os.path.join(madgraph.__path__[0], 192 "VERSION"), 193 parse_info_str, 194 print_error=False) 195 196 return info_dict
197
198 #=============================================================================== 199 # get_time_info 200 #=============================================================================== 201 -def get_time_info():
202 """Returns the present time info for use in MG5 command history header. 203 """ 204 205 creation_time = time.asctime() 206 time_info = {'time': creation_time, 207 'fill': ' ' * (26 - len(creation_time))} 208 209 return time_info
210
211 #=============================================================================== 212 # Find the subdirectory which includes the files ending with a given extension 213 #=============================================================================== 214 -def find_includes_path(start_path, extension):
215 """Browse the subdirectories of the path 'start_path' and returns the first 216 one found which contains at least one file ending with the string extension 217 given in argument.""" 218 219 subdirs=[pjoin(start_path,dir) for dir in os.listdir(start_path)] 220 for subdir in subdirs: 221 if os.path.isfile(subdir): 222 if os.path.basename(subdir).endswith(extension): 223 return start_path 224 elif os.path.isdir(subdir): 225 path = find_includes_path(subdir, extension) 226 if path: 227 return path 228 return None
229
230 #=============================================================================== 231 # Given the path of a ninja installation, this function determines if it 232 # supports quadruple precision or not. 233 #=============================================================================== 234 -def get_ninja_quad_prec_support(ninja_lib_path):
235 """ Get whether ninja supports quad prec in different ways""" 236 237 # First try with the ninja-config executable if present 238 ninja_config = os.path.abspath(pjoin( 239 ninja_lib_path,os.pardir,'bin','ninja-config')) 240 if os.path.exists(ninja_config): 241 try: 242 p = Popen([ninja_config, '-quadsupport'], stdout=subprocess.PIPE, 243 stderr=subprocess.PIPE) 244 output, error = p.communicate() 245 return 'TRUE' in output.upper() 246 except Exception: 247 pass 248 249 # If no ninja-config is present, then simply use the presence of 250 # 'quadninja' in the include 251 return False
252
253 #=============================================================================== 254 # find a executable 255 #=============================================================================== 256 -def which(program):
257 def is_exe(fpath): 258 return os.path.exists(fpath) and os.access(\ 259 os.path.realpath(fpath), os.X_OK)
260 261 if not program: 262 return None 263 264 fpath, fname = os.path.split(program) 265 if fpath: 266 if is_exe(program): 267 return program 268 else: 269 for path in os.environ["PATH"].split(os.pathsep): 270 exe_file = os.path.join(path, program) 271 if is_exe(exe_file): 272 return exe_file 273 return None 274
275 -def has_f2py():
276 has_f2py = False 277 if which('f2py'): 278 has_f2py = True 279 elif sys.version_info[1] == 6: 280 if which('f2py-2.6'): 281 has_f2py = True 282 elif which('f2py2.6'): 283 has_f2py = True 284 else: 285 if which('f2py-2.7'): 286 has_f2py = True 287 elif which('f2py2.7'): 288 has_f2py = True 289 return has_f2py
290
291 #=============================================================================== 292 # Activate dependencies if possible. Mainly for tests 293 #=============================================================================== 294 295 -def deactivate_dependence(dependency, cmd=None, log = None):
296 """ Make sure to turn off some dependency of MG5aMC. """ 297 298 def tell(msg): 299 if log == 'stdout': 300 print msg 301 elif callable(log): 302 log(msg)
303 304 305 if dependency in ['pjfry','golem','samurai','ninja']: 306 if cmd.options[dependency] not in ['None',None,'']: 307 tell("Deactivating MG5_aMC dependency '%s'"%dependency) 308 cmd.options[dependency] = None 309
310 -def activate_dependence(dependency, cmd=None, log = None, MG5dir=None):
311 """ Checks whether the specfieid MG dependency can be activated if it was 312 not turned off in MG5 options.""" 313 314 def tell(msg): 315 if log == 'stdout': 316 print msg 317 elif callable(log): 318 log(msg)
319 320 if cmd is None: 321 cmd = MGCmd.MasterCmd() 322 323 if dependency=='pjfry': 324 if cmd.options['pjfry'] in ['None',None,''] or \ 325 (cmd.options['pjfry'] == 'auto' and which_lib('libpjfry.a') is None) or\ 326 which_lib(pjoin(cmd.options['pjfry'],'libpjfry.a')) is None: 327 tell("Installing PJFry...") 328 cmd.do_install('PJFry') 329 330 if dependency=='golem': 331 if cmd.options['golem'] in ['None',None,''] or\ 332 (cmd.options['golem'] == 'auto' and which_lib('libgolem.a') is None) or\ 333 which_lib(pjoin(cmd.options['golem'],'libgolem.a')) is None: 334 tell("Installing Golem95...") 335 cmd.do_install('Golem95') 336 337 if dependency=='samurai': 338 raise MadGraph5Error, 'Samurai cannot yet be automatically installed.' 339 340 if dependency=='ninja': 341 if cmd.options['ninja'] in ['None',None,''] or\ 342 (cmd.options['ninja'] == './HEPTools/lib' and not MG5dir is None and\ 343 which_lib(pjoin(MG5dir,cmd.options['ninja'],'libninja.a')) is None): 344 tell("Installing ninja...") 345 cmd.do_install('ninja') 346
347 #=============================================================================== 348 # find a library 349 #=============================================================================== 350 -def which_lib(lib):
351 def is_lib(fpath): 352 return os.path.exists(fpath) and os.access(fpath, os.R_OK)
353 354 if not lib: 355 return None 356 357 fpath, fname = os.path.split(lib) 358 if fpath: 359 if is_lib(lib): 360 return lib 361 else: 362 locations = sum([os.environ[env_path].split(os.pathsep) for env_path in 363 ["DYLD_LIBRARY_PATH","LD_LIBRARY_PATH","LIBRARY_PATH","PATH"] 364 if env_path in os.environ],[]) 365 for path in locations: 366 lib_file = os.path.join(path, lib) 367 if is_lib(lib_file): 368 return lib_file 369 return None 370
371 #=============================================================================== 372 # Return Nice display for a random variable 373 #=============================================================================== 374 -def nice_representation(var, nb_space=0):
375 """ Return nice information on the current variable """ 376 377 #check which data to put: 378 info = [('type',type(var)),('str', var)] 379 if hasattr(var, 'func_doc'): 380 info.append( ('DOC', var.func_doc) ) 381 if hasattr(var, '__doc__'): 382 info.append( ('DOC', var.__doc__) ) 383 if hasattr(var, '__dict__'): 384 info.append( ('ATTRIBUTE', var.__dict__.keys() )) 385 386 spaces = ' ' * nb_space 387 388 outstr='' 389 for name, value in info: 390 outstr += '%s%3s : %s\n' % (spaces,name, value) 391 392 return outstr
393 394 # 395 # Decorator for re-running a crashing function automatically. 396 # 397 wait_once = False
398 -def multiple_try(nb_try=5, sleep=20):
399 400 def deco_retry(f): 401 def deco_f_retry(*args, **opt): 402 for i in range(nb_try): 403 try: 404 return f(*args, **opt) 405 except KeyboardInterrupt: 406 raise 407 except Exception, error: 408 global wait_once 409 if not wait_once: 410 text = """Start waiting for update. (more info in debug mode)""" 411 logger.info(text) 412 logger_stderr.debug('fail to do %s function with %s args. %s try on a max of %s (%s waiting time)' % 413 (str(f), ', '.join([str(a) for a in args]), i+1, nb_try, sleep * (i+1))) 414 logger_stderr.debug('error is %s' % str(error)) 415 if __debug__: logger_stderr.debug('and occurred at :'+traceback.format_exc()) 416 wait_once = True 417 time.sleep(sleep * (i+1)) 418 419 if __debug__: 420 raise 421 raise error.__class__, '[Fail %i times] \n %s ' % (i+1, error)
422 return deco_f_retry 423 return deco_retry 424
425 #=============================================================================== 426 # helper for scan. providing a nice formatted string for the scan name 427 #=============================================================================== 428 -def get_scan_name(first, last):
429 """return a name of the type xxxx[A-B]yyy 430 where xxx and yyy are the common part between the two names. 431 """ 432 433 # find the common string at the beginning 434 base = [first[i] for i in range(len(first)) if first[:i+1] == last[:i+1]] 435 # remove digit even if in common 436 while base and base[0].isdigit(): 437 base = base[1:] 438 # find the common string at the end 439 end = [first[-(i+1)] for i in range(len(first)) if first[-(i+1):] == last[-(i+1):]] 440 # remove digit even if in common 441 while end and end[-1].isdigit(): 442 end = end[:-1] 443 end.reverse() 444 #convert to string 445 base, end = ''.join(base), ''.join(end) 446 if end: 447 name = "%s[%s-%s]%s" % (base, first[len(base):-len(end)], last[len(base):-len(end)],end) 448 else: 449 name = "%s[%s-%s]%s" % (base, first[len(base):], last[len(base):],end) 450 return name
451
452 #=============================================================================== 453 # Compiler which returns smart output error in case of trouble 454 #=============================================================================== 455 -def compile(arg=[], cwd=None, mode='fortran', job_specs = True, nb_core=1 ,**opt):
456 """compile a given directory""" 457 458 cmd = ['make'] 459 try: 460 if nb_core > 1: 461 cmd.append('-j%s' % nb_core) 462 cmd += arg 463 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, 464 stderr=subprocess.STDOUT, cwd=cwd, **opt) 465 (out, err) = p.communicate() 466 except OSError, error: 467 if cwd and not os.path.exists(cwd): 468 raise OSError, 'Directory %s doesn\'t exists. Impossible to run make' % cwd 469 else: 470 error_text = "Impossible to compile %s directory\n" % cwd 471 error_text += "Trying to launch make command returns:\n" 472 error_text += " " + str(error) + "\n" 473 error_text += "In general this means that your computer is not able to compile." 474 if sys.platform == "darwin": 475 error_text += "Note that MacOSX doesn\'t have gmake/gfortan install by default.\n" 476 error_text += "Xcode3 contains those required programs" 477 raise MadGraph5Error, error_text 478 479 if p.returncode: 480 # Check that makefile exists 481 if not cwd: 482 cwd = os.getcwd() 483 all_file = [f.lower() for f in os.listdir(cwd)] 484 if 'makefile' not in all_file: 485 raise OSError, 'no makefile present in %s' % os.path.realpath(cwd) 486 487 if mode == 'fortran' and not (which('g77') or which('gfortran')): 488 error_msg = 'A fortran compiler (g77 or gfortran) is required to create this output.\n' 489 error_msg += 'Please install g77 or gfortran on your computer and retry.' 490 raise MadGraph5Error, error_msg 491 elif mode == 'cpp' and not which('g++'): 492 error_msg ='A C++ compiler (g++) is required to create this output.\n' 493 error_msg += 'Please install g++ (which is part of the gcc package) on your computer and retry.' 494 raise MadGraph5Error, error_msg 495 496 # Check if this is due to the need of gfortran 4.6 for quadruple precision 497 if any(tag.upper() in out.upper() for tag in ['real(kind=16)','real*16', 498 'complex*32']) and mode == 'fortran' and not \ 499 ''.join(get_gfortran_version().split('.')) >= '46': 500 if not which('gfortran'): 501 raise MadGraph5Error, 'The fortran compiler gfortran v4.6 or later '+\ 502 'is required to compile %s.\nPlease install it and retry.'%cwd 503 else: 504 logger_stderr.error('ERROR, you could not compile %s because'%cwd+\ 505 ' your version of gfortran is older than 4.6. MadGraph5_aMC@NLO will carry on,'+\ 506 ' but will not be able to compile an executable.') 507 return p.returncode 508 # Other reason 509 error_text = 'A compilation Error occurs ' 510 if cwd: 511 error_text += 'when trying to compile %s.\n' % cwd 512 error_text += 'The compilation fails with the following output message:\n' 513 error_text += ' '+out.replace('\n','\n ')+'\n' 514 error_text += 'Please try to fix this compilations issue and retry.\n' 515 error_text += 'Help might be found at https://answers.launchpad.net/mg5amcnlo.\n' 516 error_text += 'If you think that this is a bug, you can report this at https://bugs.launchpad.net/mg5amcnlo' 517 raise MadGraph5Error, error_text 518 return p.returncode
519
520 -def get_gfortran_version(compiler='gfortran'):
521 """ Returns the gfortran version as a string. 522 Returns '0' if it failed.""" 523 try: 524 p = Popen([compiler, '-dumpversion'], stdout=subprocess.PIPE, 525 stderr=subprocess.PIPE) 526 output, error = p.communicate() 527 version_finder=re.compile(r"(?P<version>(\d.)*\d)") 528 version = version_finder.search(output).group('version') 529 return version 530 except Exception: 531 return '0'
532
533 -def mod_compilator(directory, new='gfortran', current=None, compiler_type='gfortran'):
534 #define global regular expression 535 if type(directory)!=list: 536 directory=[directory] 537 538 #search file 539 file_to_change=find_makefile_in_dir(directory) 540 if compiler_type == 'gfortran': 541 comp_re = re.compile('^(\s*)FC\s*=\s*(.+)\s*$') 542 var = 'FC' 543 elif compiler_type == 'cpp': 544 comp_re = re.compile('^(\s*)CXX\s*=\s*(.+)\s*$') 545 var = 'CXX' 546 else: 547 MadGraph5Error, 'Unknown compiler type: %s' % compiler_type 548 549 mod = False 550 for name in file_to_change: 551 lines = open(name,'r').read().split('\n') 552 for iline, line in enumerate(lines): 553 result = comp_re.match(line) 554 if result: 555 if new != result.group(2) and '$' not in result.group(2): 556 mod = True 557 lines[iline] = result.group(1) + var + "=" + new 558 elif compiler_type == 'gfortran' and line.startswith('DEFAULT_F_COMPILER'): 559 lines[iline] = "DEFAULT_F_COMPILER = %s" % new 560 elif compiler_type == 'cpp' and line.startswith('DEFAULT_CPP_COMPILER'): 561 lines[iline] = "DEFAULT_CPP_COMPILER = %s" % new 562 563 if mod: 564 open(name,'w').write('\n'.join(lines)) 565 # reset it to change the next file 566 mod = False
567
568 #=============================================================================== 569 # mute_logger (designed to work as with statement) 570 #=============================================================================== 571 -class MuteLogger(object):
572 """mute_logger (designed to work as with statement), 573 files allow to redirect the output of the log to a given file. 574 """ 575
576 - def __init__(self, names, levels, files=None, **opt):
577 assert isinstance(names, list) 578 assert isinstance(names, list) 579 580 self.names = names 581 self.levels = levels 582 if isinstance(files, list): 583 self.files = files 584 else: 585 self.files = [files] * len(names) 586 self.logger_saved_info = {} 587 self.opts = opt
588
589 - def __enter__(self):
590 old_levels = [] 591 for name, level, path in zip(self.names, self.levels, self.files): 592 if path: 593 self.setup_logFile_for_logger(path, name, **self.opts) 594 log_module = logging.getLogger(name) 595 old_levels.append(log_module.level) 596 log_module = logging.getLogger(name) 597 log_module.setLevel(level) 598 self.levels = old_levels
599
600 - def __exit__(self, ctype, value, traceback ):
601 for name, level, path, level in zip(self.names, self.levels, self.files, self.levels): 602 if 'keep' in self.opts and not self.opts['keep']: 603 self.restore_logFile_for_logger(name, level, path=path) 604 else: 605 self.restore_logFile_for_logger(name, level) 606 607 log_module = logging.getLogger(name) 608 log_module.setLevel(level)
609
610 - def setup_logFile_for_logger(self, path, full_logname, **opts):
611 """ Setup the logger by redirecting them all to logfiles in tmp """ 612 613 logs = full_logname.split('.') 614 lognames = [ '.'.join(logs[:(len(logs)-i)]) for i in\ 615 range(len(full_logname.split('.')))] 616 for logname in lognames: 617 try: 618 os.remove(path) 619 except Exception, error: 620 pass 621 my_logger = logging.getLogger(logname) 622 hdlr = logging.FileHandler(path) 623 # I assume below that the orders of the handlers in my_logger.handlers 624 # remains the same after having added/removed the FileHandler 625 self.logger_saved_info[logname] = [hdlr, my_logger.handlers] 626 #for h in my_logger.handlers: 627 # h.setLevel(logging.CRITICAL) 628 for old_hdlr in list(my_logger.handlers): 629 my_logger.removeHandler(old_hdlr) 630 my_logger.addHandler(hdlr) 631 #my_logger.setLevel(level) 632 my_logger.debug('Log of %s' % logname)
633
634 - def restore_logFile_for_logger(self, full_logname, level, path=None, **opts):
635 """ Setup the logger by redirecting them all to logfiles in tmp """ 636 637 logs = full_logname.split('.') 638 lognames = [ '.'.join(logs[:(len(logs)-i)]) for i in\ 639 range(len(full_logname.split('.')))] 640 for logname in lognames: 641 if path: 642 try: 643 os.remove(path) 644 except Exception, error: 645 pass 646 my_logger = logging.getLogger(logname) 647 if logname in self.logger_saved_info: 648 my_logger.removeHandler(self.logger_saved_info[logname][0]) 649 for old_hdlr in self.logger_saved_info[logname][1]: 650 my_logger.addHandler(old_hdlr) 651 else: 652 my_logger.setLevel(level)
653 654 #for i, h in enumerate(my_logger.handlers): 655 # h.setLevel(cls.logger_saved_info[logname][2][i]) 656 657 nb_open =0
658 @contextlib.contextmanager 659 -def stdchannel_redirected(stdchannel, dest_filename):
660 """ 661 A context manager to temporarily redirect stdout or stderr 662 663 e.g.: 664 665 666 with stdchannel_redirected(sys.stderr, os.devnull): 667 if compiler.has_function('clock_gettime', libraries=['rt']): 668 libraries.append('rt') 669 """ 670 671 try: 672 oldstdchannel = os.dup(stdchannel.fileno()) 673 dest_file = open(dest_filename, 'w') 674 os.dup2(dest_file.fileno(), stdchannel.fileno()) 675 yield 676 finally: 677 if oldstdchannel is not None: 678 os.dup2(oldstdchannel, stdchannel.fileno()) 679 os.close(oldstdchannel) 680 if dest_file is not None: 681 dest_file.close()
682
683 -def get_open_fds():
684 ''' 685 return the number of open file descriptors for current process 686 687 .. warning: will only work on UNIX-like os-es. 688 ''' 689 import subprocess 690 import os 691 692 pid = os.getpid() 693 procs = subprocess.check_output( 694 [ "lsof", '-w', '-Ff', "-p", str( pid ) ] ) 695 nprocs = filter( 696 lambda s: s and s[ 0 ] == 'f' and s[1: ].isdigit(), 697 procs.split( '\n' ) ) 698 699 return nprocs
700
701 -def detect_if_cpp_compiler_is_clang(cpp_compiler):
702 """ Detects whether the specified C++ compiler is clang.""" 703 704 try: 705 p = Popen([cpp_compiler, '--version'], stdout=subprocess.PIPE, 706 stderr=subprocess.PIPE) 707 output, error = p.communicate() 708 except Exception, error: 709 # Cannot probe the compiler, assume not clang then 710 return False 711 return 'LLVM' in output
712
713 -def detect_cpp_std_lib_dependence(cpp_compiler):
714 """ Detects if the specified c++ compiler will normally link against the C++ 715 standard library -lc++ or -libstdc++.""" 716 717 is_clang = detect_if_cpp_compiler_is_clang(cpp_compiler) 718 if is_clang: 719 try: 720 import platform 721 v, _,_ = platform.mac_ver() 722 if not v: 723 # We will not attempt to support clang elsewhere than on macs, so 724 # we venture a guess here. 725 return '-lc++' 726 else: 727 v = float(v.rsplit('.')[1]) 728 if v >= 9: 729 return '-lc++' 730 else: 731 return '-lstdc++' 732 except: 733 return '-lstdc++' 734 return '-lstdc++'
735
736 -def detect_current_compiler(path, compiler_type='fortran'):
737 """find the current compiler for the current directory""" 738 739 # comp = re.compile("^\s*FC\s*=\s*(\w+)\s*") 740 # The regular expression below allows for compiler definition with absolute path 741 if compiler_type == 'fortran': 742 comp = re.compile("^\s*FC\s*=\s*([\w\/\\.\-]+)\s*") 743 elif compiler_type == 'cpp': 744 comp = re.compile("^\s*CXX\s*=\s*([\w\/\\.\-]+)\s*") 745 else: 746 MadGraph5Error, 'Unknown compiler type: %s' % compiler_type 747 748 for line in open(path): 749 if comp.search(line): 750 compiler = comp.search(line).groups()[0] 751 return compiler 752 elif compiler_type == 'fortran' and line.startswith('DEFAULT_F_COMPILER'): 753 return line.split('=')[1].strip() 754 elif compiler_type == 'cpp' and line.startswith('DEFAULT_CPP_COMPILER'): 755 return line.split('=')[1].strip()
756
757 -def find_makefile_in_dir(directory):
758 """ return a list of all file starting with makefile in the given directory""" 759 760 out=[] 761 #list mode 762 if type(directory)==list: 763 for name in directory: 764 out+=find_makefile_in_dir(name) 765 return out 766 767 #single mode 768 for name in os.listdir(directory): 769 if os.path.isdir(directory+'/'+name): 770 out+=find_makefile_in_dir(directory+'/'+name) 771 elif os.path.isfile(directory+'/'+name) and name.lower().startswith('makefile'): 772 out.append(directory+'/'+name) 773 elif os.path.isfile(directory+'/'+name) and name.lower().startswith('make_opt'): 774 out.append(directory+'/'+name) 775 return out
776
777 -def rm_old_compile_file():
778 779 # remove all the .o files 780 os.path.walk('.', rm_file_extension, '.o') 781 782 # remove related libraries 783 libraries = ['libblocks.a', 'libgeneric_mw.a', 'libMWPS.a', 'libtools.a', 'libdhelas3.a', 784 'libdsample.a', 'libgeneric.a', 'libmodel.a', 'libpdf.a', 'libdhelas3.so', 'libTF.a', 785 'libdsample.so', 'libgeneric.so', 'libmodel.so', 'libpdf.so'] 786 lib_pos='./lib' 787 [os.remove(os.path.join(lib_pos, lib)) for lib in libraries \ 788 if os.path.exists(os.path.join(lib_pos, lib))]
789
790 791 -def format_time(n_secs):
792 m, s = divmod(n_secs, 60) 793 h, m = divmod(m, 60) 794 d, h = divmod(h, 24) 795 if d > 0: 796 return "%d day%s,%dh%02dm%02ds" % (d,'' if d<=1 else 's',h, m, s) 797 elif h > 0: 798 return "%dh%02dm%02ds" % (h, m, s) 799 elif m > 0: 800 return "%dm%02ds" % (m, s) 801 else: 802 return "%d second%s" % (s, '' if s<=1 else 's')
803
804 -def rm_file_extension( ext, dirname, names):
805 806 [os.remove(os.path.join(dirname, name)) for name in names if name.endswith(ext)]
807
808 809 810 -def multiple_replacer(*key_values):
811 replace_dict = dict(key_values) 812 replacement_function = lambda match: replace_dict[match.group(0)] 813 pattern = re.compile("|".join([re.escape(k) for k, v in key_values]), re.M) 814 return lambda string: pattern.sub(replacement_function, string)
815
816 -def multiple_replace(string, *key_values):
817 return multiple_replacer(*key_values)(string)
818
819 # Control 820 -def check_system_error(value=1):
821 def deco_check(f): 822 def deco_f(arg, *args, **opt): 823 try: 824 return f(arg, *args, **opt) 825 except OSError, error: 826 logger.debug('try to recover from %s' % error) 827 if isinstance(arg, list): 828 prog = arg[0] 829 else: 830 prog = arg[0] 831 832 # Permission denied 833 if error.errno == 13: 834 if os.path.exists(prog): 835 os.system('chmod +x %s' % prog) 836 elif 'cwd' in opt and opt['cwd'] and \ 837 os.path.isfile(pjoin(opt['cwd'],arg[0])): 838 os.system('chmod +x %s' % pjoin(opt['cwd'],arg[0])) 839 return f(arg, *args, **opt) 840 # NO such file or directory 841 elif error.errno == 2: 842 # raise a more meaningfull error message 843 raise Exception, '%s fails with no such file or directory' \ 844 % arg 845 else: 846 raise
847 return deco_f 848 return deco_check 849
850 851 @check_system_error() 852 -def call(arg, *args, **opt):
853 """nice way to call an external program with nice error treatment""" 854 try: 855 return subprocess.call(arg, *args, **opt) 856 except OSError: 857 arg[0] = './%s' % arg[0] 858 return subprocess.call(arg, *args, **opt)
859
860 @check_system_error() 861 -def Popen(arg, *args, **opt):
862 """nice way to call an external program with nice error treatment""" 863 return subprocess.Popen(arg, *args, **opt)
864
865 @multiple_try() 866 -def mult_try_open(filepath, *args, **opt):
867 """try to open a file with multiple try to ensure that filesystem is sync""" 868 return open(filepath, *args, ** opt)
869
870 871 ################################################################################ 872 # TAIL FUNCTION 873 ################################################################################ 874 -def tail(f, n, offset=None):
875 """Reads a n lines from f with an offset of offset lines. The return 876 value is a tuple in the form ``lines``. 877 """ 878 avg_line_length = 74 879 to_read = n + (offset or 0) 880 881 while 1: 882 try: 883 f.seek(-(avg_line_length * to_read), 2) 884 except IOError: 885 # woops. apparently file is smaller than what we want 886 # to step back, go to the beginning instead 887 f.seek(0) 888 pos = f.tell() 889 lines = f.read().splitlines() 890 if len(lines) >= to_read or pos == 0: 891 return lines[-to_read:offset and -offset or None] 892 avg_line_length *= 1.3 893 avg_line_length = int(avg_line_length)
894
895 ################################################################################ 896 # LAST LINE FUNCTION 897 ################################################################################ 898 -def get_last_line(fsock):
899 """return the last line of a file""" 900 901 return tail(fsock, 1)[0]
902
903 -class BackRead(file):
904 """read a file returning the lines in reverse order for each call of readline() 905 This actually just reads blocks (4096 bytes by default) of data from the end of 906 the file and returns last line in an internal buffer.""" 907 908
909 - def readline(self):
910 """ readline in a backward way """ 911 912 while len(self.data) == 1 and ((self.blkcount * self.blksize) < self.size): 913 self.blkcount = self.blkcount + 1 914 line = self.data[0] 915 try: 916 self.seek(-self.blksize * self.blkcount, 2) # read from end of file 917 self.data = (self.read(self.blksize) + line).split('\n') 918 except IOError: # can't seek before the beginning of the file 919 self.seek(0) 920 data = self.read(self.size - (self.blksize * (self.blkcount-1))) + line 921 self.data = data.split('\n') 922 923 if len(self.data) == 0: 924 return "" 925 926 line = self.data.pop() 927 return line + '\n'
928
929 - def __init__(self, filepos, blksize=4096):
930 """initialize the internal structures""" 931 932 # get the file size 933 self.size = os.stat(filepos)[6] 934 # how big of a block to read from the file... 935 self.blksize = blksize 936 # how many blocks we've read 937 self.blkcount = 1 938 file.__init__(self, filepos, 'rb') 939 # if the file is smaller than the blocksize, read a block, 940 # otherwise, read the whole thing... 941 if self.size > self.blksize: 942 self.seek(-self.blksize * self.blkcount, 2) # read from end of file 943 self.data = self.read(self.blksize).split('\n') 944 # strip the last item if it's empty... a byproduct of the last line having 945 # a newline at the end of it 946 if not self.data[-1]: 947 self.data.pop()
948
949 - def next(self):
950 line = self.readline() 951 if line: 952 return line 953 else: 954 raise StopIteration
955
956 957 -def write_PS_input(filePath, PS):
958 """ Write out in file filePath the PS point to be read by the MadLoop.""" 959 try: 960 PSfile = open(filePath, 'w') 961 # Add a newline in the end as the implementation fortran 'read' 962 # command on some OS is problematic if it ends directly with the 963 # floating point number read. 964 965 PSfile.write('\n'.join([' '.join(['%.16E'%pi for pi in p]) \ 966 for p in PS])+'\n') 967 PSfile.close() 968 except Exception: 969 raise MadGraph5Error, 'Could not write out the PS point to file %s.'\ 970 %str(filePath)
971
972 -def format_timer(running_time):
973 """ return a nicely string representing the time elapsed.""" 974 if running_time < 2e-2: 975 running_time = running_time = 'current time: %02dh%02d' % (time.localtime().tm_hour, time.localtime().tm_min) 976 elif running_time < 10: 977 running_time = ' %.2gs ' % running_time 978 elif 60 > running_time >= 10: 979 running_time = ' %.3gs ' % running_time 980 elif 3600 > running_time >= 60: 981 running_time = ' %im %is ' % (running_time // 60, int(running_time % 60)) 982 else: 983 running_time = ' %ih %im ' % (running_time // 3600, (running_time//60 % 60)) 984 return running_time
985
986 987 #=============================================================================== 988 # TMP_directory (designed to work as with statement) 989 #=============================================================================== 990 -class TMP_directory(object):
991 """create a temporary directory and ensure this one to be cleaned. 992 """ 993
994 - def __init__(self, suffix='', prefix='tmp', dir=None):
995 self.nb_try_remove = 0 996 import tempfile 997 self.path = tempfile.mkdtemp(suffix, prefix, dir)
998 999
1000 - def __exit__(self, ctype, value, traceback ):
1001 #True only for debugging: 1002 if False and isinstance(value, Exception): 1003 sprint("Directory %s not cleaned. This directory can be removed manually" % self.path) 1004 return False 1005 try: 1006 shutil.rmtree(self.path) 1007 except OSError: 1008 self.nb_try_remove += 1 1009 if self.nb_try_remove < 3: 1010 time.sleep(10) 1011 self.__exit__(ctype, value, traceback) 1012 else: 1013 logger.warning("Directory %s not completely cleaned. This directory can be removed manually" % self.path)
1014
1015 - def __enter__(self):
1016 return self.path
1017
1018 -class TMP_variable(object):
1019 """create a temporary directory and ensure this one to be cleaned. 1020 """ 1021
1022 - def __init__(self, cls, attribute, value):
1023 1024 self.old_value = getattr(cls, attribute) 1025 self.cls = cls 1026 self.attribute = attribute 1027 setattr(self.cls, self.attribute, value)
1028
1029 - def __exit__(self, ctype, value, traceback ):
1030 1031 setattr(self.cls, self.attribute, self.old_value)
1032
1033 - def __enter__(self):
1034 return self.old_value
1035
1036 # 1037 # GUNZIP/GZIP 1038 # 1039 -def gunzip(path, keep=False, stdout=None):
1040 """ a standard replacement for os.system('gunzip -f %s.gz ' % event_path)""" 1041 1042 if not path.endswith(".gz"): 1043 if os.path.exists("%s.gz" % path): 1044 path = "%s.gz" % path 1045 else: 1046 raise Exception, "%(path)s does not finish by .gz and the file %(path)s.gz does not exists" %\ 1047 {"path": path} 1048 1049 1050 #for large file (>1G) it is faster and safer to use a separate thread 1051 if os.path.getsize(path) > 1e8: 1052 if stdout: 1053 os.system('gunzip -c %s > %s' % (path, stdout)) 1054 else: 1055 os.system('gunzip %s' % path) 1056 return 0 1057 1058 if not stdout: 1059 stdout = path[:-3] 1060 try: 1061 gfile = ziplib.open(path, "r") 1062 except IOError: 1063 raise 1064 else: 1065 try: 1066 open(stdout,'w').write(gfile.read()) 1067 except IOError: 1068 # this means that the file is actually not gzip 1069 if stdout == path: 1070 return 1071 else: 1072 files.cp(path, stdout) 1073 1074 if not keep: 1075 os.remove(path) 1076 return 0
1077
1078 -def gzip(path, stdout=None, error=True, forceexternal=False):
1079 """ a standard replacement for os.system('gzip %s ' % path)""" 1080 1081 #for large file (>1G) it is faster and safer to use a separate thread 1082 if os.path.getsize(path) > 1e9 or forceexternal: 1083 call(['gzip', '-f', path]) 1084 if stdout: 1085 if not stdout.endswith(".gz"): 1086 stdout = "%s.gz" % stdout 1087 shutil.move('%s.gz' % path, stdout) 1088 return 1089 1090 if not stdout: 1091 stdout = "%s.gz" % path 1092 elif not stdout.endswith(".gz"): 1093 stdout = "%s.gz" % stdout 1094 1095 try: 1096 ziplib.open(stdout,"w").write(open(path).read()) 1097 except OverflowError: 1098 gzip(path, stdout, error=error, forceexternal=True) 1099 except Exception: 1100 if error: 1101 raise 1102 else: 1103 os.remove(path)
1104
1105 # 1106 # Global function to open supported file types 1107 # 1108 -class open_file(object):
1109 """ a convinient class to open a file """ 1110 1111 web_browser = None 1112 eps_viewer = None 1113 text_editor = None 1114 configured = False 1115
1116 - def __init__(self, filename):
1117 """open a file""" 1118 1119 # Check that the class is correctly configure 1120 if not self.configured: 1121 self.configure() 1122 1123 try: 1124 extension = filename.rsplit('.',1)[1] 1125 except IndexError: 1126 extension = '' 1127 1128 1129 # dispatch method 1130 if extension in ['html','htm','php']: 1131 self.open_program(self.web_browser, filename, background=True) 1132 elif extension in ['ps','eps']: 1133 self.open_program(self.eps_viewer, filename, background=True) 1134 else: 1135 self.open_program(self.text_editor,filename, mac_check=False)
1136 # mac_check to False avoid to use open cmd in mac 1137 1138 @classmethod
1139 - def configure(cls, configuration=None):
1140 """ configure the way to open the file """ 1141 1142 cls.configured = True 1143 1144 # start like this is a configuration for mac 1145 cls.configure_mac(configuration) 1146 if sys.platform == 'darwin': 1147 return # done for MAC 1148 1149 # on Mac some default (eps/web) might be kept on None. This is not 1150 #suitable for LINUX which doesn't have open command. 1151 1152 # first for eps_viewer 1153 if not cls.eps_viewer: 1154 cls.eps_viewer = cls.find_valid(['evince','gv', 'ggv'], 'eps viewer') 1155 1156 # Second for web browser 1157 if not cls.web_browser: 1158 cls.web_browser = cls.find_valid( 1159 ['firefox', 'chrome', 'safari','opera'], 1160 'web browser')
1161 1162 @classmethod
1163 - def configure_mac(cls, configuration=None):
1164 """ configure the way to open a file for mac """ 1165 1166 if configuration is None: 1167 configuration = {'text_editor': None, 1168 'eps_viewer':None, 1169 'web_browser':None} 1170 1171 for key in configuration: 1172 if key == 'text_editor': 1173 # Treat text editor ONLY text base editor !! 1174 if configuration[key]: 1175 program = configuration[key].split()[0] 1176 if not which(program): 1177 logger.warning('Specified text editor %s not valid.' % \ 1178 configuration[key]) 1179 else: 1180 # All is good 1181 cls.text_editor = configuration[key] 1182 continue 1183 #Need to find a valid default 1184 if os.environ.has_key('EDITOR'): 1185 cls.text_editor = os.environ['EDITOR'] 1186 else: 1187 cls.text_editor = cls.find_valid( 1188 ['vi', 'emacs', 'vim', 'gedit', 'nano'], 1189 'text editor') 1190 1191 elif key == 'eps_viewer': 1192 if configuration[key]: 1193 cls.eps_viewer = configuration[key] 1194 continue 1195 # else keep None. For Mac this will use the open command. 1196 elif key == 'web_browser': 1197 if configuration[key]: 1198 cls.web_browser = configuration[key] 1199 continue
1200 # else keep None. For Mac this will use the open command. 1201 1202 @staticmethod
1203 - def find_valid(possibility, program='program'):
1204 """find a valid shell program in the list""" 1205 1206 for p in possibility: 1207 if which(p): 1208 logger.info('Using default %s \"%s\". ' % (program, p) + \ 1209 'Set another one in ./input/mg5_configuration.txt') 1210 return p 1211 1212 logger.info('No valid %s found. ' % program + \ 1213 'Please set in ./input/mg5_configuration.txt') 1214 return None
1215 1216
1217 - def open_program(self, program, file_path, mac_check=True, background=False):
1218 """ open a file with a given program """ 1219 1220 if mac_check==True and sys.platform == 'darwin': 1221 return self.open_mac_program(program, file_path) 1222 1223 # Shell program only 1224 if program: 1225 arguments = program.split() # allow argument in program definition 1226 arguments.append(file_path) 1227 1228 if not background: 1229 subprocess.call(arguments) 1230 else: 1231 import thread 1232 thread.start_new_thread(subprocess.call,(arguments,)) 1233 else: 1234 logger.warning('Not able to open file %s since no program configured.' % file_path + \ 1235 'Please set one in ./input/mg5_configuration.txt')
1236
1237 - def open_mac_program(self, program, file_path):
1238 """ open a text with the text editor """ 1239 1240 if not program: 1241 # Ask to mac manager 1242 os.system('open %s' % file_path) 1243 elif which(program): 1244 # shell program 1245 arguments = program.split() # Allow argument in program definition 1246 arguments.append(file_path) 1247 subprocess.call(arguments) 1248 else: 1249 # not shell program 1250 os.system('open -a %s %s' % (program, file_path))
1251
1252 -def get_HEPTools_location_setter(HEPToolsDir,type):
1253 """ Checks whether mg5dir/HEPTools/<type> (which is 'lib', 'bin' or 'include') 1254 is in the environment paths of the user. If not, it returns a preamble that 1255 sets it before calling the exectuable, for example: 1256 <preamble> ./my_exe 1257 with <preamble> -> DYLD_LIBRARY_PATH='blabla;$DYLD_LIBRARY_PATH'""" 1258 1259 assert(type in ['bin','include','lib']) 1260 1261 target_env_var = 'PATH' if type in ['bin','include'] else \ 1262 ('DYLD_LIBRARY_PATH' if sys.platform=='darwin' else 'LD_LIBRARY_PATH') 1263 1264 target_path = os.path.abspath(pjoin(HEPToolsDir,type)) 1265 1266 if target_env_var not in os.environ or \ 1267 target_path not in os.environ[target_env_var].split(os.pathsep): 1268 return "%s='%s;$%s' "%(target_env_var,target_path,target_env_var) 1269 else: 1270 return ''
1271
1272 -def get_shell_type():
1273 """ Try and guess what shell type does the user use.""" 1274 try: 1275 if os.environ['SHELL'].endswith('bash'): 1276 return 'bash' 1277 elif os.environ['SHELL'].endswith('tcsh'): 1278 return 'tcsh' 1279 else: 1280 # If unknown, return None 1281 return None 1282 except KeyError: 1283 return None
1284
1285 -def is_executable(path):
1286 """ check if a path is executable""" 1287 try: 1288 return os.access(path, os.X_OK) 1289 except Exception: 1290 return False
1291
1292 -class OptionParser(optparse.OptionParser):
1293 """Option Peaser which raise an error instead as calling exit""" 1294
1295 - def exit(self, status=0, msg=None):
1296 if msg: 1297 raise InvalidCmd, msg 1298 else: 1299 raise InvalidCmd
1300
1301 -def sprint(*args, **opt):
1302 """Returns the current line number in our program.""" 1303 1304 if not __debug__: 1305 return 1306 1307 import inspect 1308 if opt.has_key('log'): 1309 log = opt['log'] 1310 else: 1311 log = logging.getLogger('madgraph') 1312 if opt.has_key('level'): 1313 level = opt['level'] 1314 else: 1315 level = logging.getLogger('madgraph').level 1316 #print "madgraph level",level 1317 #if level == 20: 1318 # level = 10 #avoid info level 1319 #print "use", level 1320 lineno = inspect.currentframe().f_back.f_lineno 1321 fargs = inspect.getframeinfo(inspect.currentframe().f_back) 1322 filename, lineno = fargs[:2] 1323 #file = inspect.currentframe().f_back.co_filename 1324 #print type(file) 1325 try: 1326 source = inspect.getsourcelines(inspect.currentframe().f_back) 1327 line = source[0][lineno-source[1]] 1328 line = re.findall(r"misc\.sprint\(\s*(.*)\)\s*($|#)", line)[0][0] 1329 if line.startswith("'") and line.endswith("'") and line.count(",") ==0: 1330 line= '' 1331 elif line.startswith("\"") and line.endswith("\"") and line.count(",") ==0: 1332 line= '' 1333 elif line.startswith(("\"","'")) and len(args)==1 and "%" in line: 1334 line= '' 1335 except Exception: 1336 line='' 1337 1338 if line: 1339 intro = ' %s = \033[0m' % line 1340 else: 1341 intro = '' 1342 1343 1344 log.log(level, ' '.join([intro]+[str(a) for a in args]) + \ 1345 ' \033[1;30m[%s at line %s]\033[0m' % (os.path.basename(filename), lineno)) 1346 return
1347
1348 ################################################################################ 1349 # function to check if two float are approximatively equal 1350 ################################################################################ 1351 -def equal(a,b,sig_fig=6, zero_limit=True):
1352 """function to check if two float are approximatively equal""" 1353 import math 1354 1355 if not a or not b: 1356 if zero_limit: 1357 power = sig_fig + 1 1358 else: 1359 return a == b 1360 else: 1361 power = sig_fig - int(math.log10(abs(a))) + 1 1362 1363 return ( a==b or abs(int(a*10**power) - int(b*10**power)) < 10)
1364
1365 ################################################################################ 1366 # class to change directory with the "with statement" 1367 # Exemple: 1368 # with chdir(path) as path: 1369 # pass 1370 ################################################################################ 1371 -class chdir:
1372 - def __init__(self, newPath):
1373 self.newPath = newPath
1374
1375 - def __enter__(self):
1376 self.savedPath = os.getcwd() 1377 os.chdir(self.newPath)
1378
1379 - def __exit__(self, etype, value, traceback):
1380 os.chdir(self.savedPath)
1381
1382 ################################################################################ 1383 # Timeout FUNCTION 1384 ################################################################################ 1385 1386 -def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
1387 '''This function will spwan a thread and run the given function using the args, kwargs and 1388 return the given default value if the timeout_duration is exceeded 1389 ''' 1390 import threading 1391 class InterruptableThread(threading.Thread): 1392 def __init__(self): 1393 threading.Thread.__init__(self) 1394 self.result = default
1395 def run(self): 1396 try: 1397 self.result = func(*args, **kwargs) 1398 except Exception,error: 1399 print error 1400 self.result = default 1401 it = InterruptableThread() 1402 it.start() 1403 it.join(timeout_duration) 1404 return it.result 1405
1406 1407 ################################################################################ 1408 # TAIL FUNCTION 1409 ################################################################################ 1410 -class digest:
1411
1412 - def test_all(self):
1413 try: 1414 return self.test_hashlib() 1415 except Exception: 1416 pass 1417 try: 1418 return self.test_md5() 1419 except Exception: 1420 pass 1421 try: 1422 return self.test_zlib() 1423 except Exception: 1424 pass
1425
1426 - def test_hashlib(self):
1427 import hashlib 1428 def digest(text): 1429 """using mg5 for the hash""" 1430 t = hashlib.md5() 1431 t.update(text) 1432 return t.hexdigest()
1433 return digest
1434
1435 - def test_md5(self):
1436 import md5 1437 def digest(text): 1438 """using mg5 for the hash""" 1439 t = md5.md5() 1440 t.update(text) 1441 return t.hexdigest()
1442 return digest 1443
1444 - def test_zlib(self):
1445 import zlib 1446 def digest(text): 1447 return zlib.adler32(text)
1448 1449 digest = digest().test_all()
1450 1451 #=============================================================================== 1452 # Helper class for timing and RAM flashing of subprocesses. 1453 #=============================================================================== 1454 -class ProcessTimer:
1455 - def __init__(self,*args,**opts):
1456 self.cmd_args = args 1457 self.cmd_opts = opts 1458 self.execution_state = False
1459
1460 - def execute(self):
1461 self.max_vms_memory = 0 1462 self.max_rss_memory = 0 1463 1464 self.t1 = None 1465 self.t0 = time.time() 1466 self.p = subprocess.Popen(*self.cmd_args,**self.cmd_opts) 1467 self.execution_state = True
1468
1469 - def poll(self):
1470 if not self.check_execution_state(): 1471 return False 1472 1473 self.t1 = time.time() 1474 # I redirect stderr to void, because from MacOX snow leopard onward, this 1475 # ps -p command writes a million times the following stupid warning 1476 # dyld: DYLD_ environment variables being ignored because main executable (/bin/ps) is setuid or setgid 1477 flash = subprocess.Popen("ps -p %i -o rss"%self.p.pid, 1478 shell=True,stdout=subprocess.PIPE,stderr=open(os.devnull,"w")) 1479 stdout_list = flash.communicate()[0].split('\n') 1480 rss_memory = int(stdout_list[1]) 1481 # for now we ignore vms 1482 vms_memory = 0 1483 1484 # This is the neat version using psutil 1485 # try: 1486 # pp = psutil.Process(self.p.pid) 1487 # 1488 # # obtain a list of the subprocess and all its descendants 1489 # descendants = list(pp.get_children(recursive=True)) 1490 # descendants = descendants + [pp] 1491 # 1492 # rss_memory = 0 1493 # vms_memory = 0 1494 # 1495 # # calculate and sum up the memory of the subprocess and all its descendants 1496 # for descendant in descendants: 1497 # try: 1498 # mem_info = descendant.get_memory_info() 1499 # 1500 # rss_memory += mem_info[0] 1501 # vms_memory += mem_info[1] 1502 # except psutil.error.NoSuchProcess: 1503 # # sometimes a subprocess descendant will have terminated between the time 1504 # # we obtain a list of descendants, and the time we actually poll this 1505 # # descendant's memory usage. 1506 # pass 1507 # 1508 # except psutil.error.NoSuchProcess: 1509 # return self.check_execution_state() 1510 1511 self.max_vms_memory = max(self.max_vms_memory,vms_memory) 1512 self.max_rss_memory = max(self.max_rss_memory,rss_memory) 1513 1514 return self.check_execution_state()
1515
1516 - def is_running(self):
1517 # Version with psutil 1518 # return psutil.pid_exists(self.p.pid) and self.p.poll() == None 1519 return self.p.poll() == None
1520
1521 - def check_execution_state(self):
1522 if not self.execution_state: 1523 return False 1524 if self.is_running(): 1525 return True 1526 self.executation_state = False 1527 self.t1 = time.time() 1528 return False
1529
1530 - def close(self,kill=False):
1531 1532 if self.p.poll() == None: 1533 if kill: 1534 self.p.kill() 1535 else: 1536 self.p.terminate()
1537 1538 # Again a neater handling with psutil 1539 # try: 1540 # pp = psutil.Process(self.p.pid) 1541 # if kill: 1542 # pp.kill() 1543 # else: 1544 # pp.terminate() 1545 # except psutil.error.NoSuchProcess: 1546 # pass 1547 1548 1549 try: 1550 import Foundation 1551 import objc 1552 NSUserNotification = objc.lookUpClass('NSUserNotification') 1553 NSUserNotificationCenter = objc.lookUpClass('NSUserNotificationCenter')
1554 1555 - def apple_notify(subtitle, info_text, userInfo={}):
1556 try: 1557 notification = NSUserNotification.alloc().init() 1558 notification.setTitle_('MadGraph5_aMC@NLO') 1559 notification.setSubtitle_(subtitle) 1560 notification.setInformativeText_(info_text) 1561 notification.setUserInfo_(userInfo) 1562 NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(notification) 1563 except: 1564 pass
1565 except:
1566 - def apple_notify(subtitle, info_text, userInfo={}):
1567 return
1568