def ask_question(self, question, chars='', default='', on_intr=''): """Prompt user for input to a question.""" # add hints of what can be entered if chars: question += ' (%s)' % ('/'.join(chars.upper())) if default: question += ' [%s]' % default self.in_question = True try: try: # see set_status() for an explanation of the special chars here ans = self.readline( '\x01\r\x1b[K' + colorize('bold', '\x02# ' + question + ' \x01') + '\x02', add_history=False) except (KeyboardInterrupt, EOFError): return on_intr if chars: # we accept any string; if it's beginning with one of the chars, # that is the result, otherwise it's the default value ans = ans.lower() for char in chars: if ans.startswith(char): return char return default if not ans: ans = default return ans finally: self.in_question = False
def print_where(self): """Implements the "/where" command.""" if self.status in ('running', 'paused'): self.put_client('Printing current script.') for i, line in enumerate(self.current_script): if i + 1 == self.current_line: self.put(colorize('darkgreen', '---> ' + line)) else: self.put(' ' + line) self.put_client('End of script.') if self.cur_eta: self.put_client('Estimated finishing time: ' + self.cur_eta) else: self.put_client('No script is running.')
def ask_input(self, question): """Prompt user for a line of input.""" self.in_question = True try: try: # see set_status() for an explanation of the special chars here ans = self.readline('\x01\r\x1b[K' + colorize('bold', '\x02# ' + question + ' \x01') + '\x02', add_history=False) except (KeyboardInterrupt, EOFError): return '' return ans finally: self.in_question = False
def debug_repl(self): """Called to handle remote debugging via Rpdb.""" self.in_question = True # suppress prompt changes try: while self.debug_mode: try: cmd = self.readline('\x01\r\x1b[K' + colorize( 'darkred', '\x02# (Rpdb) \x01') + '\x02') + '\n' except (EOFError, KeyboardInterrupt): cmd = '' except StateChange: if not self.debug_mode: return self.tell('debuginput', cmd) finally: self.in_question = False
def set_status(self, status): """Update the current execution status, and set a new prompt.""" self.status = status if self.stop_pending: pending = ' (stop pending)' elif self.pending_requests: pending = ' (%d pending)' % len(self.pending_requests) else: pending = '' # \x01/\x02 are markers recognized by readline as "here come" # zero-width control characters; ESC[K means "clear whole line" self.prompt = '\x01' + colorize( self.stcolmap[status], '\r\x1b[K\x02# ' + (self.instrument or '') + '[%s%s]%s %s \x01' % (self.modemap[self.current_mode], status, pending, self.spy_mode and 'spy>' or '>>')) + '\x02' os.write(self.wakeup_pipe_w, b' ')
def cancel_menu(self, arg): if not self.pending_requests: self.put_client('No scripts or commands are pending.') return if arg == '*': self.tell('unqueue', '*') return self.put_client('Showing pending scripts or commands.') indices = {} for index, (reqid, short) in enumerate(self._iter_pending(), start=1): indices[index] = reqid self.put('# %s %s' % (colorize('blue', '%2d' % index), short)) res = self.ask_question('Which script to cancel ("*" for all)?') if res == '*': self.tell('unqueue', '*') return try: reqid = indices[int(res)] except (ValueError, KeyError): self.put_error('Invalid selection.') return self.tell('unqueue', reqid)
def put_message(self, msg, sim=False): """Handles the "message" signal.""" if msg[0] == 'nicos': namefmt = '' else: namefmt = '%-10s: ' % msg[0] levelno = msg[2] if levelno == ACTION: action = namefmt + msg[3].rstrip() self.out.write('\x1b]0;NICOS%s\x07' % (action and ' (%s)' % action or '')) return else: if self.subsec_ts: timesuf = '.%.06d] ' % ((msg[1] % 1) * 1000000) else: timesuf = '] ' if levelno <= DEBUG: timefmt = strftime('[%H:%M:%S', localtime(msg[1])) newtext = colorize('lightgray', timefmt + timesuf) + \ colorize('darkgray', namefmt + msg[3].rstrip()) elif levelno <= INFO: timefmt = strftime('[%H:%M:%S', localtime(msg[1])) newtext = colorize('lightgray', timefmt + timesuf) + \ namefmt + msg[3].rstrip() elif levelno == INPUT: newtext = colorize('darkgreen', msg[3].rstrip()) elif levelno <= WARNING: timefmt = strftime('[%Y-%m-%d %H:%M:%S', localtime(msg[1])) newtext = colorize( 'purple', timefmt + timesuf + namefmt + levels[levelno] + ': ' + msg[3].rstrip()) else: timefmt = strftime('[%Y-%m-%d %H:%M:%S', localtime(msg[1])) newtext = colorize( 'red', timefmt + timesuf + namefmt + levels[levelno] + ': ' + msg[3].rstrip()) if sim: newtext = '(sim) ' + newtext self.put(newtext)
def command(self, cmd, arg): """Called when a "/foo" command is entered at the prompt.""" # try to order elif cases by frequency if cmd in ('cmd', 'exec'): if cmd == 'cmd' and self.spy_mode: return self.command('eval', arg) # this is not usually entered as "/cmd foo", but only "foo" if self.status in ('running', 'paused'): reply = self.ask_question( 'A script is already running, ' 'queue or execute anyway?', chars='qxn') if reply == 'x': if self.status != 'idle': self.tell('exec', arg) else: self.run(arg) elif reply == 'q': self.run(arg) self.put_client('Command queued.') else: self.run(arg) elif cmd in ('r', 'run', 'run!'): if not arg: # since we remember the last edited file, we can offer # running it here if self.last_filename: reply = self.ask_question( 'Run last used file %r?' % path.basename(self.last_filename), chars='yn', default='y') if reply == 'y': self.command('run', self.last_filename) return self.put_error('Need a file name as argument.') return fpath = path.join(self.scriptpath, path.expanduser(arg)) try: code = open(fpath).read() except Exception as e: self.put_error('Unable to open file: %s.' % e) return if self.status in ('running', 'paused') and cmd != 'run!': if self.ask_question( 'A script is already running, ' 'queue script?', chars='yn', default='y') == 'y': self.run(code, fpath) else: self.run(code, fpath) elif cmd == 'update': if not arg: # always take the current filename, if it still exists if path.isfile(self.current_filename): arg = self.current_filename if not arg: self.put_error('Need a file name as argument.') return fpath = path.join(self.scriptpath, path.expanduser(arg)) try: code = open(fpath).read() except Exception as e: self.put_error('Unable to open file: %s.' % e) return reason = self.ask_input('Reason for updating:') self.tell('update', code, reason) elif cmd in ('sim', 'simulate'): if not arg: self.put_error('Need a file name or code as argument.') return fpath = path.join(self.scriptpath, path.expanduser(arg)) self.last_filename = fpath # detect whether we have a filename or potential Python code if path.isfile(fpath) or fpath.endswith(('.py', '.txt')): try: code = open(fpath).read() except Exception as e: self.put_error('Unable to open file: %s.' % e) return self.simulate(fpath, code) else: self.simulate('', arg) elif cmd in ('e', 'edit'): self.edit_file(arg) elif cmd == 'break': self.tell('break', BREAK_AFTER_STEP) elif cmd in ('cont', 'continue'): self.tell('continue') elif cmd in ('s', 'stop'): if self.status == 'running': self.stop_query('Stop request') else: self.tell('emergency') elif cmd in ('fin', 'finish'): self.tell('finish') elif cmd == 'pending': self.show_pending() elif cmd == 'cancel': self.cancel_menu(arg) elif cmd == 'disconnect': if self.isconnected: self.disconnect() elif cmd == 'connect': self.reconnect_count = 0 if self.isconnected: self.put_error('Already connected. Use /disconnect first.') else: self.ask_connect() elif cmd in ('re', 'reconnect'): self.reconnect_count = 0 # no automatic reconnect if self.isconnected: self.disconnect() self.ask_connect(ask_all=False) elif cmd in ('q', 'quit'): if self.isconnected: self.disconnect() return 0 # i.e. exit with success elif cmd in ('h', 'help', '?'): self.help(arg) elif cmd == 'log': if arg: n = str(int(arg)) # make sure it's an integer else: n = '*' # as a slice index, this means "unlimited" # this can take a while to transfer, but we don't want to cache # messages in this client just for this command messages = self.ask('getmessages', n) if messages is None: return self.put_client('Printing %s previous messages.' % (n if n != '*' else 'all')) for msg in messages: self.put_message(msg) self.put_client('End of messages.') elif cmd in ('w', 'where'): self.print_where() elif cmd == 'wait': if arg: time.sleep(float(arg)) else: # this command is mainly meant for testing and scripting purposes time.sleep(0.1) while self.status != 'idle': time.sleep(0.1) elif cmd == 'trace': trace = self.ask('gettrace') if trace is None: return self.put_client('Current stacktrace of script execution:') for line in trace.splitlines(): if line: self.put('# ' + line) self.put_client('End of stacktrace.') elif cmd == 'debugclient': import pdb pdb.set_trace() elif cmd == 'debug': self.tell('debug', arg) elif cmd == 'eval': timefmt = colorize('lightgray', strftime('[%H:%M:%S]')) self.put('%s -> %s' % (timefmt, self.eval(arg, None, stringify=True))) elif cmd == 'spy': if not self.spy_mode: self.put_client( 'Spy mode on: normal input is evaluated as ' 'an expression, use /exec to execute as script.') else: self.put_client('Spy mode off.') self.spy_mode = not self.spy_mode self.set_status(self.status) elif cmd == 'plot': self.plot_data(xterm_mode=(arg == 'x')) elif cmd == 'subsec': self.subsec_ts = not self.subsec_ts else: self.put_error('Unknown command %r.' % cmd)
def ask_passwd(self, question): """Prompt user for a password.""" return getpass.getpass(colorize('bold', '# %s ' % question))
def put_client(self, string): """Put a client info message.""" self.put(colorize('bold', '# ' + string))
def put_error(self, string): """Put a client error message.""" self.put(colorize('red', '# ERROR: ' + string))