def complete(self, text, state): # pylint: disable=unused-argument ''' Tab complete for the current step. ''' if state == 0: parser = StatementParser(self.features) begidx = readline.get_begidx() endidx = readline.get_endidx() line = readline.get_line_buffer() prefix = line[begidx:endidx] if line else '' line = line[:endidx] if self.complete_single_token: # treat the entire line as a single token args = [line] else: # tokenize the line tokens = parser.tokenize(line) tokens = parser.condense(tokens) args = [t.text for t in tokens if isinstance(t, StringToken)] self.completions = self.active_step.complete(self, args, prefix) if state < len(self.completions): return self.completions[state] return None
def __init__(self, shell_name='pypsi', width=80, exit_rc=-1024, ctx=None): ''' :param str shell_name: the name of the shell; used in error messages :param int exit_rc: the exit return code that is returned from a command when the shell needs to end execution :param pypsi.namespace.Namespace ctx: the base context ''' self.real_stdout = sys.stdout self.real_stdin = sys.stdin self.real_stderr = sys.stderr self.width = width self.shell_name = shell_name self.exit_rc = exit_rc self.errno = 0 self.commands = {} self.preprocessors = [] self.postprocessors = [] self.plugins = [] self.prompt = "{name} )> ".format(name=shell_name) self.ctx = ctx or Namespace() self.parser = StatementParser() self.default_cmd = None self.register_base_plugins() self.fallback_cmd = None self.on_shell_ready()
def preprocess_single(self, raw, origin): tokens = self.on_tokenize([StringToken(0, raw, quote='"')], origin) if tokens: parser = StatementParser(self.features) parser.clean_escapes(tokens) ret = '' for token in tokens: ret += token.text return ret return ''
def __init__(self, name, description, steps=None): ''' :param str name: the prompt wizard name to display to the user :param str description: a short description of what the wizard does :param list steps: a list of :class:`WizardStep` objects ''' self.name = name self.description = description self.steps = steps self.values = Namespace() self.parser = StatementParser()
def __init__(self, shell_name='pypsi', width=79, exit_rc=-1024, ctx=None): ''' Subclasses need to call the Shell constructor to properly initialize it. :param str shell_name: the name of the shell; used in error messages :param int exit_rc: the exit return code that is returned from a command when the shell needs to end execution :param pypsi.namespace.Namespace ctx: the base context ''' self.backup_stdout = None self.backup_stdin = None self.backup_stderr = None self.backup_print = None self.width = width self.shell_name = shell_name self.exit_rc = exit_rc self.errno = 0 self.commands = {} self.preprocessors = [] self.postprocessors = [] self.plugins = [] self.prompt = "{name} )> ".format(name=shell_name) self.ctx = ctx or Namespace() self.parser = StatementParser() self.default_cmd = None self.register_base_plugins() self.fallback_cmd = None self.eof_is_sigint = False self._backup_completer = readline.get_completer() self.bootstrap() self.on_shell_ready()
def get_completions(self, line, prefix): ''' Get the list of completions given a line buffer and a prefix. :param str line: line buffer content up to cursor :param str prefix: readline prefix token :returns list[str]: list of completions ''' try: parser = StatementParser(TabCompletionFeatures(self.features)) tokens = parser.tokenize(line) parser.clean_escapes(tokens) cmd_name = "" loc = None args = [] next_arg = True ret = [] in_quote = None for token in tokens: if isinstance(token, StringToken): in_quote = token.quote if token.open_quote else None if not cmd_name: cmd_name = token.text loc = 'name' elif loc == 'name': cmd_name += token.text else: if next_arg: args.append(token.text) next_arg = False else: args[-1] += token.text elif isinstance(token, OperatorToken): in_quote = None if token.operator in ('|', ';', '&&', '||'): cmd_name = None args = [] next_arg = True elif token.operator in ('>', '<', '>>'): loc = 'path' args = [] elif isinstance(token, WhitespaceToken): in_quote = None if loc == 'name': loc = None next_arg = True if loc == 'path': ret = path_completer(''.join(args), prefix) elif not cmd_name or loc == 'name': if is_path_prefix(cmd_name): ret = path_completer(cmd_name, prefix) else: ret = self.get_command_name_completions(cmd_name) else: if cmd_name not in self.commands: ret = [] else: if next_arg: args.append('') cmd = self.commands[cmd_name] ret = cmd.complete(self, args, prefix) ret = self._clean_completions(ret, in_quote) except: ret = [] return ret
def execute(self, raw): ''' Parse and execute a statement. :param str raw: the raw command line to parse. :param function input: a function that returns a string, overrides default input function (stdin). :returns int: the return code of the statement. ''' parser = StatementParser(self.features) input_complete = False while not input_complete: text = self.preprocess(raw, 'input') if text is None: return None try: tokens = parser.tokenize(text) except (UnclosedQuotationError, TrailingEscapeError): input_complete = False else: # Parsing succeeded, break out of the input loop input_complete = True if not input_complete: # This is a multiline input try: # hide prompt if reading from a file raw = input("> " if sys.stdin.isatty() else '') except (EOFError, KeyboardInterrupt) as e: self.on_input_canceled() raise e tokens = self.on_tokenize(tokens, 'input') statement = None if not tokens: return None try: statement = parser.build(tokens) except StatementSyntaxError as e: self.error(str(e)) return 1 rc = None if not statement: # The line was empty, a comment, or just contained whitespace. return rc # Setup the invocations for invoke in statement: try: # Open any and all I/O redirections and resolve the pypsi # command. invoke.setup(self) except Exception as e: for sub in statement: sub.close_streams() if isinstance(e, (IORedirectionError, CommandNotFoundError)): # pypsi can handle I/O redirection and command not found # errors, these are not fatal. self.error(str(e)) return -1 # Unhandled fatal exception, re-raise it raise # Current pipe being built pipe = [] # Process the statement for invoke in statement: if invoke.chain_pipe(): # We are in a pipe pipe.append(invoke) else: # We are not in a pipe if pipe: # We have a pipe built that needs to be executed. # Create the invocation threads for the pipe. threads, stdin = self.create_pipe_threads(pipe) # Reset the building pipe pipe = [] # Set the current invocation's stdin to the last # invocation's stdout. invoke.stdin = stdin else: # We were not in a pipe threads = [] # Start all the pipe threads, if we are processing a pipe for t in threads: t.start() # Execute the invocation in the current thread. try: rc = invoke(self) except Exception as e: # Unhandled exception, stop all threads if any are running. for t in threads: t.stop() # Wait for threads to terminate. try: for t in threads: t.join() except: # Something went wrong or a KeyboardInterrupt was # issued. Stop waiting for threads to terminate. pass # Print thread-specific unhandled exceptions. for t in threads: if t.exc_info: if t.exc_info[0] == OSError: msg = t.exc_info[1].strerror else: msg = str(t.exc_info[1]) print(AnsiCodes.red, t.invoke.name, ": ", msg, AnsiCodes.reset, sep='') if isinstance(e, KeyboardInterrupt): # Ctrl+c was entered print() rc = -1 elif isinstance(e, SystemExit): # The command is requesting to exit the shell. rc = e.code # pylint: disable=no-member print("exiting....") self.running = False elif isinstance(e, RuntimeError): # The command was aborted by a generic exception. self.error("command aborted: " + str(e)) rc = -1 else: # Unhandled fatal exception, re-raise it raise self.errno = rc # Check if the statement's next invocation be executed. if not invoke.should_continue(rc): break return rc
def __init__(self, name, description, steps=None): self.name = name self.description = description self.steps = steps self.values = Namespace() self.parser = StatementParser()