def _process_directive(self, name, value, indent): # only top-level directives can be split off into jobs. Also, no jobs will be # split off if the max_jobs value is < 2. So under these conditions, we start # the directive directly within this process if self._we_are_child or self.top_unit != self or self._max_jobs < 2: return self._start_directive(name, value, indent) # if the max number of jobs is already running, wait for one to finish while len(self._child_jobs) >= self._max_jobs: _dprint(2, os.getpid(), "waiting for a child to finish") pid, status = os.wait() self._reap_child_process(pid, status) # create temporary log files for child process loggers = self._allocate_tmp_loggers() pid = os.fork() if pid: # parent branch _dprint(2, os.getpid(), "forked off child", pid, "for", name, value) self._child_jobs[pid] = loggers # ignore everything related to this directive self._ignore_until_indent = indent else: # child branch self._we_are_child = True self._child_jobs = {} self._set_tmp_loggers(loggers) self._start_directive(name, value, indent)
def _reap_child_process (self,pid,status): _dprint(2,os.getpid(),"child",pid,"exited with status",status); if pid not in self._child_jobs: raise RuntimeError,"unexpected child pid "+str(pid); loggers = self._child_jobs.pop(pid); self._merge_tmp_loggers(loggers); # fail if child did not return 0 if status: self.fail("child process returns status %d"%status);
def _start_directive (self,name,value,indent,ignore=False): _dprint(3,os.getpid(),"starting directive",name,value); # find class by that name tu_class = self.top_unit.Directives.get(name,None); if not callable(tu_class): raise Trut.ParseError,"unknown directive '%s'"%name; # create test unit self.top_unit = tu = tu_class(value,parent=self.top_unit); self._new_topunit(tu,indent);
def _start_directive(self, name, value, indent, ignore=False): _dprint(3, os.getpid(), "starting directive", name, value) # find class by that name tu_class = self.top_unit.Directives.get(name, None) if not callable(tu_class): raise Trut.ParseError("unknown directive '%s'" % name) # create test unit self.top_unit = tu = tu_class(value, parent=self.top_unit) self._new_topunit(tu, indent)
def _reap_child_process(self, pid, status): _dprint(2, os.getpid(), "child", pid, "exited with status", status) if pid not in self._child_jobs: raise RuntimeError("unexpected child pid " + str(pid)) loggers = self._child_jobs.pop(pid) self._merge_tmp_loggers(loggers) # fail if child did not return 0 if status: self.fail("child process returns status %d" % status)
def execute(self): self.parent.compile_script(need_mqs=True) if self.parent.giveup(): self.success("SKIP") return _dprint(1, "running TDL job", self.name) jobfunc = getattr(self._module, self.name, None) if not callable(jobfunc): self.fail("TDL job not found") jobfunc(self._mqs, None, wait=True)
def log_exc(self, exctype, excvalue, exctb, level=0): """writes exception, if the specified level is <= the verbosity level we were created with.""" # STDERR.write("***exc %s %d %d\n"%(exctype,level,self.verbose)); if level <= self.verbose: _dprint(5, os.getpid(), "logging to file", self.fileobj, excvalue) traceback.print_exception(exctype, excvalue, exctb, limit=None, file=self.fileobj)
def execute (self): self.parent.compile_script(need_mqs=True); if self.parent.giveup(): self.success("SKIP"); return; _dprint(1,"running TDL job",self.name); jobfunc = getattr(self._module,self.name,None); if not callable(jobfunc): self.fail("TDL job not found"); jobfunc(self._mqs,None,wait=True);
def __init__ (self,name,parent=None): _dprint(2,"new unit",name,self.__class__); self.name = name; self.parent = parent; self.failed = None; self._fail_logged = False; self._success_logged = False; # inherit parent's options, and persistence level (-1) if parent: self.options = dmi.record(**parent.options); self.persist = parent.persist - 1; else: self.options = dmi.record(); self.persist = 0;
def __init__(self, name, parent=None): _dprint(2, "new unit", name, self.__class__) self.name = name self.parent = parent self.failed = None self._fail_logged = False self._success_logged = False # inherit parent's options, and persistence level (-1) if parent: self.options = dmi.record(**parent.options) self.persist = parent.persist - 1 else: self.options = dmi.record() self.persist = 0
def merge_file (self,logfile): """merges the given file into our log."""; _dprint(2,"merging in logfile",logfile); if logfile is None: return None; if isinstance(logfile,str): self.fileobj.writelines(file(logfile,'r')); elif isinstance(logfile,file): # logfile.seek(0); # for line in logfile: # _dprint(0,os.getpid(),"merging from",logfile,line); logfile.seek(0); self.fileobj.writelines(logfile); else: raise TypeError,"'logfile' should be a filename or a file object";
def merge_file(self, logfile): """merges the given file into our log.""" _dprint(2, "merging in logfile", logfile) if logfile is None: return None import six, io if isinstance(logfile, str): self.fileobj.writelines(open(logfile, 'r')) elif isinstance(logfile, file if six.PY2 else io.IOBase): # logfile.seek(0); # for line in logfile: # _dprint(0,os.getpid(),"merging from",logfile,line); logfile.seek(0) self.fileobj.writelines(logfile) else: raise TypeError("'logfile' should be a filename or a file object")
def log (self,message,status="",level=0,progress=False): """writes message and optional status, if the specified level is <= the verbosity level we were created with."""; if level > self.verbose: return; # progress messages only go to consoles if progress: if self._is_console: self.fileobj.write("%-70.70s\r"%message); self.fileobj.flush(); # normal messages go according to level else: _dprint(5,os.getpid(),"logging to file",self.fileobj,level,message,status); if status: self.fileobj.write("%-70s [%s]\n"%(message,status)); else: self.fileobj.write("%-80s\n"%message);
def exec_top_unit (self): try: self.top_unit.execute(); except: excinfo = sys.exc_info(); self.log_exc(*excinfo); self.top_unit.fail(excinfo[1]); try: self.top_unit.cleanup(); except: excinfo = sys.exc_info(); self.log_exc(*excinfo); self.top_unit.fail(excinfo[1]); excinfo = None; clsname,name = self.top_unit.__class__.__name__,self.top_unit.name; _dprint(3,os.getpid(),"finished directive",clsname,name,"fail is",self.failed); self.top_unit = self.top_unit.parent; # if we are a child process, and we're finished with this unit, exit if self._we_are_child and self.top_unit == self: sys.exit(self.failed);
def compile_script(self, need_mqs=False): """compiles the script, if not already compiled. If need_mqs=True, starts a meqserver and builds the tree as well""" if not self._module: self._compile_failed = True # will reset to False if all goes well\ self.log_progress("compile") # start meqserver if required if (need_mqs or int(self.get_option("start_meqserver", 0))) and not self._mqs: from Timba.Apps import meqserver # get multithreading option mt = int(self.get_option("multithreaded", 0)) if mt > 1: extra = ["-mt", str(mt)] else: extra = [] _dprint(1, "starting meqserver", extra) self._mqs = meqserver.default_mqs(wait_init=10, extra=extra) from Timba.TDL import Compile from Timba.TDL import TDLOptions # load config file tdlconf = self.get_option("tdlconf", ".tdl.conf") TDLOptions.config.read(tdlconf) TDLOptions.init_options(self.name, save=False) # compile TDL module _dprint(1, "compiling TDL script", self.name) try: (self._module, ns, msg) = Compile.compile_file(self._mqs, self.name) except: excinfo = sys.exc_info() self.log_exc(level=2, *excinfo) self.fail("compile failed") excinfo = None return # success self.log(msg, level=2) self.success("compile") self._compile_failed = False pass
def compile_script (self,need_mqs=False): """compiles the script, if not already compiled. If need_mqs=True, starts a meqserver and builds the tree as well"""; if not self._module: self._compile_failed = True; # will reset to False if all goes well\ self.log_progress("compile"); # start meqserver if required if ( need_mqs or int(self.get_option("start_meqserver",0)) ) and not self._mqs: from Timba.Apps import meqserver # get multithreading option mt = int(self.get_option("multithreaded",0)); if mt>1: extra = [ "-mt",str(mt) ]; else: extra = [] _dprint(1,"starting meqserver",extra); self._mqs = meqserver.default_mqs(wait_init=10,extra=extra); from Timba.TDL import Compile from Timba.TDL import TDLOptions # load config file tdlconf = self.get_option("tdlconf",".tdl.conf"); TDLOptions.config.read(tdlconf); TDLOptions.init_options(self.name,save=False); # compile TDL module _dprint(1,"compiling TDL script",self.name); try: (self._module,ns,msg) = Compile.compile_file(self._mqs,self.name); except: excinfo = sys.exc_info(); self.log_exc(level=2,*excinfo); self.fail("compile failed"); excinfo = None; return; # success self.log(msg,level=2); self.success("compile"); self._compile_failed = False; pass;
def _process_directive (self,name,value,indent): # only top-level directives can be split off into jobs. Also, no jobs will be # split off if the max_jobs value is < 2. So under these conditions, we start # the directive directly within this process if self._we_are_child or self.top_unit != self or self._max_jobs < 2: return self._start_directive(name,value,indent); # if the max number of jobs is already running, wait for one to finish while len(self._child_jobs) >= self._max_jobs: _dprint(2,os.getpid(),"waiting for a child to finish"); pid,status = os.wait(); self._reap_child_process(pid,status); # create temporary log files for child process loggers = self._allocate_tmp_loggers(); pid = os.fork(); if pid: # parent branch _dprint(2,os.getpid(),"forked off child",pid,"for",name,value); self._child_jobs[pid] = loggers; # ignore everything related to this directive self._ignore_until_indent = indent; else: # child branch self._we_are_child = True; self._child_jobs = {}; self._set_tmp_loggers(loggers); self._start_directive(name,value,indent);
def cleanup (self): # terminate meqserver if self._mqs: _dprint(1,"halting meqserver"); self._mqs.halt();
def log_message (self,message,status,level=1,progress=False): _dprint(5,os.getpid(),"logging message",level,message,status); for logger in self.loggers: logger.log(message,status,level=level,progress=progress);
def _reap_all_children(self): while self._child_jobs: _dprint(3, os.getpid(), "waiting for all children to finish") pid, status = os.wait() self._reap_child_process(pid, status)
def log_exc (self,exctype,excvalue,exctb,level=0): """writes exception, if the specified level is <= the verbosity level we were created with."""; # STDERR.write("***exc %s %d %d\n"%(exctype,level,self.verbose)); if level <= self.verbose: _dprint(5,os.getpid(),"logging to file",self.fileobj,excvalue); traceback.print_exception(exctype,excvalue,exctb,limit=None,file=self.fileobj);
def log_message(self, message, status, level=1, progress=False): _dprint(5, os.getpid(), "logging message", level, message, status) for logger in self.loggers: logger.log(message, status, level=level, progress=progress)
def execute(self): # log a message about running the tests, at level 10 (i.e. the "error" # level of the child units) self.log('start', level=10) self._new_topunit(self, -1) # cd to directory of file dirname = os.path.dirname(self.name) os.chdir(dirname) _dprint(1, "changing directory to", dirname) self._old_path = sys.path sys.path.append('.') _dprint(1, "running trut file", self.name) # parse file try: for line in open(os.path.basename(self.name)): # check for fails if self.giveup(): break # strip comments line = line.rstrip() line = _re_comment.sub('', line) _dprint(2, os.getpid(), "line is:", line) # find initial whitespace match_indent = _re_indent.match(line) if not match_indent: # empty line _dprint(3, os.getpid(), "empty line") continue # get current indent level indent = len(match_indent.group(1).replace('\t', ' ' * 8)) # get the remaining text, strip trailing space line = match_indent.group(2) _dprint(3, os.getpid(), "indent level", indent) # if this section is being processed by a child process, ignore if self._ignore_until_indent is not None and indent > self._ignore_until_indent: _dprint(3, os.getpid(), "ignoring line -- handled by child process" "") continue # check indent level and close all relevant stanzas self._check_indent(indent) # check for an Option = Value string match = _re_option.match(line) if match: option = match.group(1) value = match.group(2).rstrip() _dprint(3, os.getpid(), "option", option, "=", value) # the top stanza is the one to which this option pertains self.top_unit.set_option(option, value) continue # check for directive match = _re_directive.match(line) if match: name = match.group(1) value = match.group(2).rstrip() self._process_directive(name, value, indent) # execute any remaining test units while self.top_unit != self and not self.giveup(): self.exec_top_unit() # wait for child jobs to finish before reporting success or failure self._reap_all_children() # failure deferred due to persistency will be reported here self.success("finish") # catch exceptions and fail except: errtype, err, tb = sys.exc_info() # re-raise if exiting if isinstance(err, SystemExit): raise err # log self.log_exc(errtype, err, tb, level=0) tb = None # avoids garbage collection delay # kill children if interrupted if isinstance(err, KeyboardInterrupt): for pid in self._child_jobs.keys(): os.kill(pid, signal.SIGINT) self.fail(err) if self._we_are_child: sys.exit(2) # wait for any child jobs to finish self._reap_all_children()
def log_exc(self, exctype, excvalue, exctb, level=1): _dprint(5, os.getpid(), "logging exception", excvalue) for logger in self.loggers: logger.log_exc(exctype, excvalue, exctb, level=level)
def cleanup(self): # terminate meqserver if self._mqs: _dprint(1, "halting meqserver") self._mqs.halt()
def log_exc (self,exctype,excvalue,exctb,level=1): _dprint(5,os.getpid(),"logging exception",excvalue); for logger in self.loggers: logger.log_exc(exctype,excvalue,exctb,level=level);
def execute (self): # log a message about running the tests, at level 10 (i.e. the "error" # level of the child units) self.log('start',level=10); self._new_topunit(self,-1); # cd to directory of file dirname = os.path.dirname(self.name); os.chdir(dirname); _dprint(1,"changing directory to",dirname); self._old_path = sys.path; sys.path.append('.'); _dprint(1,"running trut file",self.name); # parse file try: for line in file(os.path.basename(self.name)): # check for fails if self.giveup(): break; # strip comments line = line.rstrip(); line = _re_comment.sub('',line); _dprint(2,os.getpid(),"line is:",line); # find initial whitespace match_indent = _re_indent.match(line); if not match_indent: # empty line _dprint(3,os.getpid(),"empty line"); continue; # get current indent level indent = len(match_indent.group(1).replace('\t',' '*8)); # get the remaining text, strip trailing space line = match_indent.group(2); _dprint(3,os.getpid(),"indent level",indent); # if this section is being processed by a child process, ignore if self._ignore_until_indent is not None and indent > self._ignore_until_indent: _dprint(3,os.getpid(),"ignoring line -- handled by child process"""); continue; # check indent level and close all relevant stanzas self._check_indent(indent); # check for an Option = Value string match = _re_option.match(line); if match: option = match.group(1); value = match.group(2).rstrip(); _dprint(3,os.getpid(),"option",option,"=",value); # the top stanza is the one to which this option pertains self.top_unit.set_option(option,value); continue; # check for directive match = _re_directive.match(line); if match: name = match.group(1); value = match.group(2).rstrip(); self._process_directive(name,value,indent); # execute any remaining test units while self.top_unit != self and not self.giveup(): self.exec_top_unit(); # wait for child jobs to finish before reporting success or failure self._reap_all_children(); # failure deferred due to persistency will be reported here self.success("finish"); # catch exceptions and fail except: errtype,err,tb = sys.exc_info(); # re-raise if exiting if isinstance(err,SystemExit): raise err; # log self.log_exc(errtype,err,tb,level=0); tb = None; # avoids garbage collection delay # kill children if interrupted if isinstance(err,KeyboardInterrupt): for pid in self._child_jobs.iterkeys(): os.kill(pid,signal.SIGINT); self.fail(err); if self._we_are_child: sys.exit(2); # wait for any child jobs to finish self._reap_all_children();
def _reap_all_children(self): while self._child_jobs: _dprint(3,os.getpid(),"waiting for all children to finish"); pid,status = os.wait(); self._reap_child_process(pid,status);