def exec_script(self, code, user, handler): # execute code in the script namespace (this is called not from # the script thread, but from a handle thread) self.thread_data.user = user temp_request = ScriptRequest(code, None, user) temp_request.parse() session.log.log(INPUT, formatScript(temp_request, '---')) self.last_handler = weakref.ref(handler) try: for block in temp_request.code: exec(block, self.namespace) finally: self.last_handler = None
def update(self, text, reason, controller, user): """Update the code with a new script. This method is called from a different thread than execute(), so we must unset the _run flag before doing anything to self.curblock, self.code or self.blocks. """ if not self.blocks: raise ScriptError('cannot update single-line script') text = fixupScript(text) newcode, newblocks = splitBlocks(text) # stop execution after the current block self._run.clear() curblock = self.curblock # this may be off by one try: # make sure that everything that has already been executed matches if curblock >= len(newblocks): # insufficient number of new blocks raise ScriptError('new script too short') # compare all executed blocks for i in range(curblock + 1): if not self._compare(self.blocks[i], newblocks[i]): raise ScriptError('new script differs in already executed ' 'part of the code') # everything is ok, replace the script and the remaining blocks self.text = text session.scriptEvent('update', (self.name, self.text)) # also set the updating user as the new user of the script self.user = user if session._experiment and session.mode == MASTER: scr = list(session.experiment.scripts) # convert readonly list scr[self._exp_script_index] = self.text session.experiment.scripts = scr updateLinecache('<script>', text) self.code, self.blocks = newcode, newblocks self.resetSimstate() # let the client know of the update controller.eventfunc('processing', self.serialize()) updatemsg = 'UPDATE (%s)' % reason if reason else 'UPDATE' session.log.log(INPUT, formatScript(self, updatemsg)) finally: # let the script continue execution in any case self._run.set()
def execute(self, controller): """Execute the script in the given namespace, using "controller" to execute individual blocks. """ session.scriptEvent('start', (self.name, self.text)) session.countloop_request = None # reset any pause flag from before # this is to allow the traceback module to report the script's # source code correctly updateLinecache('<script>', self.text) # note: checking session._experiment since using session.experiment # would try to create the device, which means you can't execute any # command when the experiment fails if session._experiment and session.mode == MASTER: session.experiment.scripts += [self.text] self._exp_script_index = len(session.experiment.scripts) - 1 if self.name: session.elogEvent('scriptbegin', self.name) session.beginActionScope(path.basename(self.name)) # notify clients of "input" session.log.log(INPUT, formatScript(self)) try: while self.curblock < len(self.code) - 1: self._run.wait() self.curblock += 1 self.blockStart = time.time() self.emitETA(controller) controller.start_exec(self.code[self.curblock], controller.namespace, None, self.settrace) finally: if self.name: session.endActionScope() if session._experiment and session.mode == MASTER: session.experiment.scripts = session.experiment.scripts[:-1] if self.name: session.elogEvent('scriptend', self.name)
def script_thread_entry(self): """The script execution thread entry point. This thread executes setup code, then waits for scripts on self.queue. The script is then executed in the context of self.namespace, using the controller (self) to watch execution. """ self.log.debug('script_thread (re)started') session.script_thread_id = current_thread().ident try: self.namespace['NicosSetup'] = self._setup # and put it in the queue as the first request request = ScriptRequest('NicosSetup()', 'setting up NICOS', system_user, quiet=True, format='py') self.new_request(request, notify=False) while 1: # get a script (or other request) from the queue request = self.queue.get() self.thread_data.user = request.user if isinstance(request, EmergencyStopRequest): self.log.info('executing estop request from %s', request.user.name) self.execute_estop(request.user.name) continue elif not isinstance(request, ScriptRequest): self.log.error('unknown request: %s', request) continue self.log.info('processing script %s by %s', request.reqid, request.user.name) self.reqid_work = request.reqid if session.cache and self.autosim: self.simulate_request(request, quiet=True) request.setSimstate('running') # notify clients that we're processing this request now self.eventfunc('processing', request.serialize()) # parse the script and split it into blocks try: self.current_script = request self.current_script.parse() except Exception: session.log.log(INPUT, formatScript(request)) session.logUnhandledException(cut_frames=1) continue try: self.current_script.execute(self) except ControlStop as err: if err.args[0] == 'emergency stop': # block all pending requests (should have been done # already, but to be on the safe side do it here again) self.block_all_requests() self.execute_estop(err.args[2]) else: # in this case, we have already blocked all scripts # queued before the "stop" command was given; scripts # that are queued after that should be executed, so # we don't block requests here session.log.info('Script stopped by %s', err.args[2]) except BdbQuit as err: # pylint: disable=bad-except-order session.log.error('Script stopped through debugger') except Exception as err: # pylint: disable=bad-except-order # the topmost two frames are still in the # daemon, so don't display them to the user # perhaps also send an error notification try: session.scriptEvent('exception', sys.exc_info()) except Exception: # last resort: do not exit script thread even if we # can't handle this exception pass if self.debugger: self.debug_end(tracing=False) session.clearActions() session.scriptEvent('finish', None) except Exception: self.log.exception('unhandled exception in script thread') session.log.error('internal error in NICOS daemon, please restart') finally: self.thread = None