import threading import io import sys import os import os.path import traceback import logging import ast from damaris.data import Resultable from damaris.gui.ExperimentHandling import StopExperiment, _SandboxStdout logger = logging.getLogger("damaris.result_handling") class ResultHandling(threading.Thread): """ runs the result script in sandbox """ def __init__(self, script_data, result_iterator, data_pool, log_callback=None): threading.Thread.__init__(self,name="result handler") self.script=script_data self.results=result_iterator self.data_space=data_pool self.quit_flag=self.results.quit_flag self._log_callback = log_callback if self.data_space is not None: self.data_space["__recentresult"]=-1 def run(self): # Redirect stdout/stderr so print() in sandboxed scripts goes to the GUI _orig_stdout = sys.stdout _orig_stderr = sys.stderr if self._log_callback is not None: sys.stdout = _SandboxStdout(self._log_callback) sys.stderr = _SandboxStdout(self._log_callback) # execute it dataspace={} data_classes = __import__('damaris.data', dataspace, dataspace, ['*']) for name in dir(data_classes): if name[:2]=="__" and name[-2:]=="__": continue dataspace[name]=data_classes.__dict__[name] del data_classes dataspace["results"]=self dataspace["data"]=self.data_space dataspace["sleep"]=self.sleep self.raised_exception=None self.location = None # check for time.sleep() try: tree = ast.parse(self.script) for node in ast.walk(tree): found_blocking_sleep = False if isinstance(node, ast.Call): func = node.func if isinstance(func, ast.Attribute) and func.attr == 'sleep': if isinstance(func.value, ast.Name) and func.value.id == 'time': found_blocking_sleep = True elif isinstance(node, ast.ImportFrom) and node.module == 'time': for alias in node.names: if alias.name == 'sleep': found_blocking_sleep = True if found_blocking_sleep: logger.warning("time.sleep() detected in result script. This is blocking and not interruptible. Please use sleep() instead.") break except Exception: pass try: exec(self.script, dataspace) except StopExperiment: return except Exception as e: self.raised_exception=e self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3] traceback_file=io.StringIO() traceback.print_tb(sys.exc_info()[2], None, traceback_file) self.traceback=traceback_file.getvalue() traceback_file=None return if not "result" in dataspace: dataspace=None return try: dataspace["result"]() except StopExperiment: pass except Exception as e: self.raised_exception=e self.location=traceback.extract_tb(sys.exc_info()[2])[-1][1:3] traceback_file=io.StringIO() traceback.print_tb(sys.exc_info()[2], None, traceback_file) self.traceback=traceback_file.getvalue() traceback_file=None dataspace=None # Restore stdout/stderr sys.stdout = _orig_stdout sys.stderr = _orig_stderr def sleep(self, seconds): self.quit_flag.wait(seconds) if self.quit_flag.isSet(): raise StopExperiment def __iter__(self): if self.quit_flag.isSet(): self.results=None return for i in self.results: if hasattr(self.results, "in_advance"): self.data_space["__resultsinadvance"]=self.results.in_advance if self.quit_flag.isSet(): self.results=None return if isinstance(i, Resultable.Resultable): if self.data_space is not None: self.data_space["__recentresult"]=i.job_id+0 yield i if self.quit_flag.isSet(): self.results=None return def stop(self): self.quit_flag.set()